CodeGym /Cours Java /Python SELF FR /Méthodes asynchrones

Méthodes asynchrones

Python SELF FR
Niveau 25 , Leçon 2
Disponible

3.1 Module asyncio

Créer ses propres threads pour des sous-tâches asynchrones, plus personne ne fait ça. Enfin, on pourrait, mais ces actions sont considérées comme trop bas niveau et ne sont utilisées que par les développeurs de frameworks. Et encore, seulement quand c'est vraiment indispensable.

La mode aujourd'hui est à la programmation asynchrone, les opérateurs async/await et les coroutines avec tasks. Mais parlons-en dans l'ordre...

Un peu d'histoire

Au départ, en Python, pour résoudre des tâches de programmation asynchrone, on utilisait des coroutines basées sur des générateurs. Ensuite, avec Python 3.4, un module asyncio (parfois écrit async IO) est apparu, mettant en œuvre des mécanismes de programmation asynchrone. En Python 3.5 est apparue la construction async/await.

Maintenant, un peu d'introduction. Je vais d'abord te parler brièvement de toutes ces choses, puis plus en détail. Parce que presque tout fonctionne ensemble, et il est impossible d'expliquer un concept sans faire référence aux autres.

Module asyncio

Le module asyncio est destiné à écrire des programmes asynchrones, offrant la possibilité d'exécuter des tâches en parallèle. Il prend en charge les opérations d'entrée-sortie asynchrones, les minuteries, les sockets, l'exécution de coroutines et le multithreading, fonctionnant dans une ou plusieurs boucles d'événements.

Coroutines (Coroutines)

Les coroutines sont des fonctions asynchrones, définies avec le mot-clé async def. Les coroutines permettent de suspendre leur exécution avec le mot-clé await, ce qui permet à d'autres coroutines de s'exécuter pendant ce temps.


import asyncio

# Définition d'une fonction asynchrone (coroutine)
async def main():
    print('Hello ...')
    # Suspend l'exécution pendant 1 seconde
    await asyncio.sleep(1)
    print('... World!')

# Lance la fonction asynchrone main() dans la boucle d'événements
asyncio.run(main())

Boucle d'événements (Event Loop)

La boucle d'événements gère l'exécution des coroutines, des tâches et d'autres opérations asynchrones. L'appel de asyncio.run() lance la boucle d'événements et exécute la coroutine jusqu'à sa terminaison.


import asyncio

async def main():
    print('Hello ...')
    await asyncio.sleep(1)
    print('... World!')

# Obtenir la boucle d'événements actuelle
loop = asyncio.get_event_loop()
# Exécuter la coroutine jusqu'à sa fin
loop.run_until_complete(main())
# Fermer la boucle d'événements après la fin de toutes les tâches
loop.close()

Tâches (Tasks)

Les tâches permettent d'exécuter des coroutines en parallèle. Elles sont créées avec asyncio.create_task() ou asyncio.ensure_future().


import asyncio

# Définition d'une coroutine qui sera exécutée avec un délai
async def say_after(delay, what):
    # Suspend l'exécution pour un délai donné
    await asyncio.sleep(delay)
    print(what)

# Coroutine principale
async def main():
    # Créer des tâches pour exécuter les coroutines en parallèle
    task1 = asyncio.create_task(say_after(1, 'hello'))
    task2 = asyncio.create_task(say_after(2, 'world'))
    
    # Attendre la fin des deux tâches
    await task1
    await task2

# Lance la coroutine principale
asyncio.run(main())

Futures (Futures)

Les objets Future représentent les résultats des opérations asynchrones qui seront disponibles à l'avenir. Par exemple, ils sont utilisés pour attendre la fin d'une tâche asynchrone.


import asyncio

# Définition d'une coroutine qui simule une longue tâche
async def long_running_task():
    print('Task started')
    # Suspend l'exécution pendant 3 secondes
    await asyncio.sleep(3)
    print('Task finished')
    return 'Result'

# Coroutine principale
async def main():
    # Créer un future pour attendre la fin de la tâche
    future = asyncio.ensure_future(long_running_task())
    # Attendre la fin de la tâche et obtenir le résultat
    result = await future  
    print(f'Task result: {result}')

# Lance la coroutine principale
asyncio.run(main())

3.2 Fonction asynchrone — async def

Une fonction asynchrone se déclare comme une fonction normale, mais avant le mot-clé def, il faut écrire le mot async.


async def NomDeLaFonction(paramètres):
    code de la fonction

Une fonction asynchrone se déclare comme une normale, se déclenche comme une normale, mais elle renvoie un résultat différent. Si tu appelles une fonction asynchrone, elle renverra non pas un résultat, mais un objet spécial — coroutine.

On peut même vérifier cela :


import asyncio

async def main():
    print("Hello World")
            
# Appel de la fonction asynchrone, qui renvoie une coroutine
result = main()
# Vérifier le type du résultat
print(type(result)) # <class 'coroutine'>

Que se passe-t-il ? Lorsque tu annotes une fonction avec le mot async, tu lui ajoutes en fait un décorateur qui fait à peu près cela :


def async_decorator(func):
    # Créer un objet Task
    task = Task()
    # Lui passer notre fonction func pour qu'il l'exécute
    task.target = func  
    # Ajouter l'objet task à la file des tâches — Event Loop
    eventloop.add_task(task)  
    # Retourner l'objet task
    return task 

Et ton code devient similaire à :


import asyncio

@async_decorator
def main():
    print("Hello World")
            
result = main()
print(type(result)) # <class 'coroutine'>

Le sens de cette analogie est le suivant :

Quand tu appelles une fonction asynchrone, un objet spécial Task est créé, qui exécutera ta fonction, mais à un moment dans le futur. Peut-être dans 0.0001 sec, ou peut-être dans 10.

Ce objet task est ce qui te revient immédiatement en tant que résultat de l'appel de ta fonction asynchrone. C'est la coroutine. Ta fonction asynchrone peut-être n'a même pas encore commencé à être exécutée, et tu as déjà l'objet task (coroutine).

Pourquoi as-tu besoin de ce task (coroutine) ? Tu ne peux pas faire grand-chose avec, mais tu peux faire 3 choses :

  • Attendre que la fonction asynchrone s'exécute.
  • Attendre que la fonction asynchrone finisse de s'exécuter et obtenir le résultat de la fonction à partir de la coroutine.
  • Attendre que 10 (ou un nombre quelconque) de fonctions asynchrones s'exécutent.

Je vais t'expliquer comment faire cela ci-dessous.

3.3 Opérateur await

La plupart des actions avec une coroutine commence par « attendre que la fonction asynchrone s'exécute ». C'est pourquoi pour cette action, nous avons un opérateur spécial await.

Il te suffit de l'écrire devant la coroutine :


await coroutine

Ou directement devant l'appel de la fonction asynchrone :


await fonction_asynchrone(arguments)

Quand Python rencontre dans le code l'opérateur await, il suspend l'exécution de la fonction actuelle et attend que la coroutine se termine — jusqu'à ce que la fonction asynchrone à laquelle la coroutine fait référence soit terminée.

Important! L'opérateur await est utilisé uniquement à l'intérieur d'une fonction asynchrone pour suspendre l'exécution jusqu'à ce que la coroutine ou l'opération asynchrone soit terminée.

C'est fait pour simplifier le processus de commutation entre les appels de fonctions asynchrones. Un appel await — c'est en fait une déclaration « ici, nous allons attendre indéfiniment — occupez-vous d'exécuter les autres fonctions asynchrones ».

Exemple :


import asyncio

# Définition d'une fonction asynchrone
async def async_print(text):
    print(text)
        
# Fonction asynchrone principale
async def main():
    # Utilise await pour attendre l'exécution de la fonction asynchrone
    await async_print("Hello World")
        
# Lance la boucle d'événements principale et exécute la coroutine main()
asyncio.run(main()) #lance la fonction asynchrone

En réalité, l'opérateur await fonctionne de manière encore plus astucieuse — il retourne également le résultat de l'exécution de la fonction asynchrone sur laquelle il a été appelé.

Exemple :


import asyncio

# Définition d'une fonction asynchrone qui additionne deux nombres
async def async_add(a, b):
    return a + b
        
# Fonction asynchrone principale
async def main():
    # Utilise await pour obtenir le résultat de l'exécution de async_add
    sum = await async_add(100, 200)
    print(sum)
        
# Lance la boucle d'événements principale et exécute la coroutine main()
asyncio.run(main()) #lance la fonction asynchrone

Donc, en résumé, l'opérateur await :

  • Suspend la fonction asynchrone actuelle jusqu'à ce que la coroutine ou l'opération asynchrone soit terminée.
  • Retourne le résultat de l'exécution de l'opération asynchrone ou de la coroutine.
  • Ne peut être utilisé qu'à l'intérieur d'une fonction asynchrone.
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION