Comprensiones asincrónicas de Python: ¿cómo funcionan?
asynchronous list-comprehension (2)
Creo que entiendo que la función
aiter()
se llama asíncronamente, de modo que cada iteración deaiter
puede continuar sin que la anterior necesariamente vuelva todavía (¿o es este conocimiento erróneo?).
Esa comprensión es incorrecta. Las iteraciones de un async for
loop no se pueden realizar en paralelo. async for
es tan secuencial como un bucle for
normal.
La parte asincrónica de async for
es que permite que el iterador await
en nombre de la corutina iterando sobre él. Solo se puede usar en corutinas asincrónicas, y solo para uso en iteraciones asincrónicas especiales. Aparte de eso, es principalmente como un ciclo regular.
Tengo problemas para entender el uso de las comprensiones asincrónicas introducidas en Python 3.6. Como descargo de responsabilidad, no tengo mucha experiencia tratando con el código asíncrono en general en Python.
El ejemplo presentado en el documento de novedades de Python 3.6 es:
result = [i async for i in aiter() if i % 2]
En el PEP , esto se expande a:
result = []
async for i in aiter():
if i % 2:
result.append(i)
Creo que entiendo que la función aiter()
se llama asíncronamente, de modo que cada iteración de aiter
puede continuar sin que la anterior necesariamente vuelva todavía (¿o es este conocimiento erróneo?).
De lo que no estoy seguro es de cómo eso se traduce a la comprensión de la lista aquí. ¿Los resultados se colocan en la lista en el orden en que se devuelven? ¿O hay ''marcadores de posición'' efectivos en la lista final para que cada resultado se coloque en la lista en el orden correcto? ¿O estoy pensando en esto de la manera incorrecta?
Además, ¿alguien puede proporcionar un ejemplo del mundo real que ilustre tanto un caso de uso aplicable como la mecánica básica de async
en comprensiones como esta?
Básicamente, estás preguntando cómo funciona un async for
loop en un ciclo regular. Que ahora puede usar dicho ciclo en una lista de comprensión no hace ninguna diferencia aquí; eso es solo una optimización que evita las list.append()
repetidas de list.append()
, exactamente como lo hace una lista de comprensión normal.
Una async for
bucle, entonces, simplemente espera cada paso siguiente del protocolo de iteración, donde un bucle regular for
se bloquearía.
Para ilustrar, imagine un ciclo for
normal:
for foo in bar:
...
Para este ciclo, Python esencialmente hace esto:
bar_iter = iter(bar)
while True:
try:
foo = next(bar_iter)
except StopIteration:
break
...
La next(bar_iter)
llamada next(bar_iter)
no es asincrónica; bloquea.
Ahora reemplace for
async for
y en lo que Python hace cambios:
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
...
En el ejemplo anterior, aiter()
y anext()
son funciones ficticias; estos son equivalentes funcionalmente exactos de sus hermanos iter()
y next()
pero en lugar de __iter__
y __next__
estos usan __aiter__
y __anext__
. Es decir, existen enlaces asincrónicos para la misma funcionalidad, pero se distinguen de sus variantes no asincrónicas por el prefijo a
.
La palabra clave await
allí es la diferencia crucial, por lo que para cada iteración un async for
loop cede el control para que otras corrutinas puedan ejecutarse en su lugar.
De nuevo, para volver a iterar, todo esto ya se agregó en Python 3.5 (ver PEP 492 ), todo lo nuevo en Python 3.6 es que también se puede usar dicho bucle en una lista de comprensión. Y en las expresiones de generador y en las de set y dict, para el caso.
Por último, pero no menos importante, el mismo conjunto de cambios también hizo posible usar await <expression>
en la sección de expresión de una comprensión, por lo tanto:
[await func(i) for i in someiterable]
ahora es posible