Ответ 1
В основном вы спрашиваете, как цикл async for
работает над регулярным циклом. То, что вы теперь можете использовать такой цикл в понимании списка, здесь не имеет никакого значения; что просто оптимизация, которая позволяет избежать повторных вызовов list.append()
, точно так же, как и в обычном понимании списка.
Затем цикл async for
просто ждет каждый следующий шаг протокола итерации, где будет блокироваться обычный цикл for
.
Чтобы проиллюстрировать, представьте обычный цикл for
:
for foo in bar:
...
Для этого цикла Python по существу делает это:
bar_iter = iter(bar)
while True:
try:
foo = next(bar_iter)
except StopIteration:
break
...
Вызов next(bar_iter)
не является асинхронным; он блокирует.
Теперь замените for
на async for
, и что делает Python:
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
...
В приведенном выше примере aiter()
и anext()
являются вымышленными функциями; это функционально точные эквиваленты их браков iter()
и next()
, но вместо __iter__
и __next__
они используют __aiter__
и __anext__
. То есть, асинхронные крючки существуют для одной и той же функциональности, но отличаются от их неасинхронных вариантов префиксом a
.
В ключе await
имеется ключевое различие, поэтому для каждой итерации цикл async for
дает управление, поэтому вместо него могут выполняться другие сопрограммы.
Опять же, чтобы повторить итерацию, все это уже было добавлено в Python 3.5 (см. PEP 492), все, что является новым в Python 3.6 что вы можете использовать такой цикл в понимании списка. И в выражениях генераторов, и в понимании набора и диктата, если на то пошло.
И последнее, но не менее важное: тот же набор изменений также позволил использовать await <expression>
в разделе выражения понимания, поэтому:
[await func(i) for i in someiterable]
теперь возможно.