web-dev-qa-db-fra.com

Python Compréhensions asynchrones - comment fonctionnent-elles?

J'ai du mal à comprendre l'utilisation des compréhensions asynchrones introduites dans Python 3.6. Comme avertissement, je n'ai pas beaucoup d'expérience avec le code asynchrone en général en Python.

L'exemple donné dans le document Quoi de neuf pour Python 3.6 est:

result = [i async for i in aiter() if i % 2]

Dans le PEP , ceci est développé pour:

result = []
async for i in aiter():
    if i % 2:
        result.append(i)

Je pense Je comprends que la fonction aiter() est appelée de manière asynchrone, de sorte que chaque itération de aiter peut se poursuivre sans la précédente revient nécessairement (ou cette compréhension est-elle erronée?).

Ce dont je ne suis pas sûr, c'est de savoir comment cela se traduit ensuite par la compréhension de la liste ici. Les résultats sont-ils placés dans la liste dans l'ordre où ils sont renvoyés? Ou existe-t-il des "espaces réservés" efficaces dans la liste finale afin que chaque résultat soit placé dans la liste dans le bon ordre? Ou est-ce que je pense à ça dans le mauvais sens?

De plus, quelqu'un peut-il fournir un exemple concret qui illustrerait à la fois un cas d'utilisation applicable et les mécanismes de base de async dans des interprétations comme celle-ci?

30
Andrew Guy

Vous demandez essentiellement comment fonctionne une boucle async for Sur une boucle régulière. Que vous puissiez maintenant utiliser une telle boucle dans une compréhension de liste ne fait aucune différence ici; c'est juste une optimisation qui évite les appels répétés de list.append(), exactement comme le fait une compréhension de liste normale.

Une boucle async for Attend alors simplement chaque étape suivante du protocole d'itération, où une boucle for régulière se bloquerait.

Pour illustrer, imaginez une boucle for normale:

for foo in bar:
    ...

Pour cette boucle, Python fait essentiellement ceci:

bar_iter = iter(bar)
while True:
    try:
        foo = next(bar_iter)
    except StopIteration:
        break
    ...

L'appel next(bar_iter) n'est pas asynchrone; ça bloque.

Remplacez maintenant for par async for, Et ce que Python change en:

bar_iter = aiter(bar)  # aiter doesn't exist, but see below
while True:
    try:
        foo = await anext(bar_iter)  # anext doesn't exist, but see below
    except StopIteration:
        break
    ...

Dans l'exemple ci-dessus, aiter() et anext() sont des fonctions fictives; ce sont des équivalents fonctionnellement exacts de leurs frères iter() et next() mais au lieu de __iter__ et __next__, ils utilisent __aiter__ et __anext__. C'est-à-dire que les hooks asynchrones existent pour la même fonctionnalité mais se distinguent de leurs variantes non asynchrones par le préfixe a.

Le mot clé await présente la différence cruciale, donc pour chaque itération une boucle async for Donne le contrôle pour que d'autres coroutines puissent s'exécuter à la place.

Encore une fois, pour réitérer, tout cela a déjà été ajouté dans Python 3.5 (voir PEP 492 ), tout ce qui est nouveau dans Python 3.6 est que vous peut également utiliser une telle boucle dans une liste de compréhension. Et dans les expressions de générateur et les compréhensions de set et dict, d'ailleurs.

Enfin et surtout, le même ensemble de modifications a également permis d'utiliser await <expression> Dans la section expression d'une compréhension, donc:

[await func(i) for i in someiterable]

est désormais possible.

20
Martijn Pieters

I think Je comprends que la fonction aiter() est appelée de manière asynchrone, de sorte que chaque itération de aiter peut se poursuivre sans que la précédente ne revienne nécessairement encore (ou cette compréhension faux?).

Cette compréhension est fausse. Itérations d'un async for la boucle ne peut pas être effectuée en parallèle. async for est tout aussi séquentiel qu'une boucle for normale.

La partie asynchrone de async for est qu'il laisse l'itérateur await au nom de la coroutine itérer dessus. Il est uniquement destiné à être utilisé dans des coroutines asynchrones et uniquement pour une utilisation sur des itérables asynchrones spéciales. À part cela, c'est surtout comme une boucle for régulière.

11
user2357112