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?
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.
I think Je comprends que la fonction
aiter()
est appelée de manière asynchrone, de sorte que chaque itération deaiter
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.