ProgrammationDéveloppeur Backend

Expliquez comment la programmation asynchrone est réalisée en Python à l'aide de async/await. Quels sont les avantages et les difficultés de cette approche ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

La programmation asynchrone est apparue dans le langage Python à partir de la version 3.5 avec l'introduction des mots-clés async et await. Initialement, des bibliothèques telles que asyncio et des générateurs basés sur des coroutines étaient utilisées pour des tâches asynchrones, ce qui était difficile à comprendre et à maintenir. Avec l'apparition de la syntaxe async/await, le code asynchrone est devenu plus explicite et lisible, se rapprochant du style synchrone habituel.

Aspect historique

Avant l'apparition de async/await, l'asynchrone était réalisé à travers des callbacks et des générateurs (par exemple, en utilisant la bibliothèque tornado ou des API anciennes d'asyncio). Ce code était difficile à déboguer et à maintenir.

Problème

Le principal problème lors du traitement d'un grand nombre d'opérations I/O simultanées (requêtes réseau, entrée/sortie de fichiers) dans du code synchrone est le blocage du fil principal. Cela entraîne une baisse de performance et une incapacité à utiliser efficacement les ressources.

Solution

La programmation asynchrone à l'aide de async/await permet d'exécuter de nombreuses opérations d'entrée-sortie en parallèle dans un seul fil, évitant ainsi les blocages. De plus, la syntaxe est proche des fonctions normales, ce qui facilite la lisibilité et le débogage.

Exemple de code :

import asyncio async def fetch_data(delay): print(f"Début de la récupération après {delay}s de délai") await asyncio.sleep(delay) print(f"Récupération terminée après {delay}s de délai") return delay async def main(): results = await asyncio.gather( fetch_data(1), fetch_data(2), fetch_data(3) ) print("Résultats :", results) asyncio.run(main())

Caractéristiques clés :

  • Pas de blocage du fil : les opérations d'entrée-sortie libèrent le fil.
  • Syntaxe explicite (async/await) pour les appels asynchrones.
  • Intégration facile avec les bibliothèques prenant en charge asyncio.

Questions pièges.

Une fonction définie avec async def peut-elle être appelée comme une fonction normale ?

Non. L'appel de cette fonction renvoie un objet coroutine qui n'est pas exécuté jusqu'à ce qu'il soit passé au boucle d'événements (par exemple, à l'aide de await ou asyncio.run()).

def foo(): return 42 async def bar(): return 42 print(foo()) # 42 print(bar()) # <coroutine object bar at ...>

Peut-on utiliser await en dehors d'une fonction asynchrone ?

Non. Le mot-clé await doit être utilisé uniquement à l'intérieur des fonctions déclarées avec async def. Tenter d'utiliser await en dehors de cette fonction déclenchera une SyntaxError.

# Erreur ! await asyncio.sleep(1) # SyntaxError: 'await' en dehors d'une fonction asynchrone

L'asynchrone fonctionne-t-il pour des opérations non liées à l'I/O (par exemple, des calculs) ?

Non. L'asynchrone est efficace uniquement pour les opérations d'entrée-sortie. Pour les tâches de calcul, le multiprocessing ou le threading sont toujours nécessaires, sinon la boucle d'événements sera bloquée.

Erreurs typiques et anti-patterns

Avantages :

  • Réduit considérablement le temps d'attente pour les opérations I/O.
  • Améliore la réactivité des serveurs et des applications.
  • Conserve la nature monothread du code, évitant les problèmes de multithreading.

Inconvénients :

  • Le code asynchrone est difficile à tester.
  • Nécessite le support de l'asynchrone par les bibliothèques (toutes ne prennent pas en charge asyncio).
  • Illusion que l'asynchrone accélère les calculs — cela est vrai uniquement pour I/O.

Exemple de la vie réelle

Cas négatif : Des jeunes développeurs ont décidé d'accélérer l'application avec async/await, mais faisaient des calculs de manière asynchrone, et non des requêtes réseau. L'application ne s'est pas accélérée. Avantages : ils se sont familiarisés avec la syntaxe. Inconvénients : pas de profit, code devenu plus complexe.

Cas positif : Ils ont traité de manière asynchrone des milliers de requêtes à l'API. Le serveur a pu servir plus de clients sans augmenter les ressources. Avantages : la performance a considérablement augmenté, l'architecture est devenue plus simple. Inconvénients : la barre d'entrée pour les débutants a augmenté.