2017-02-20 35 views
25

У меня возникли проблемы с пониманием использования асинхронных понятий, представленных в Python 3.6. Как отказ от ответственности, у меня нет большого опыта работы с асинхронным кодом вообще в Python.Python Asynchronous Comprehensions - как они работают?

Пример, приведенный в what's new for Python 3.6 документе:

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

В PEP, это расширяется до:

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

Я думаю Я понимаю, что функция aiter() вызывается асинхронно, так что каждая итерация aiter может продолжаться без предыдущей, которая обязательно возвращается (или это понимание неверно?).

То, что я не уверен, так это то, что это переводит на понимание списка здесь. Устанавливаются ли результаты в список в том порядке, в котором они возвращаются? Или есть эффективные «заполнители» в конечном списке, чтобы каждый результат был помещен в список в правильном порядке? Или я думаю об этом неправильно?

Кроме того, кто-то может представить пример реального мира, который иллюстрирует как применимый прецедент, так и основную механику async в понимании подобного?

+0

Мне также интересны генераторы async. Такое же поведение или другое? –

ответ

16

Вы в основном спрашиваете, как цикл 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] 

теперь возможно.

+0

Спасибо Martijn за подробный ответ. Таким образом, цикл 'async for' ведет себя так же, как обычный цикл' for', за исключением того, что управление итерацией цикла передается в сопроводительную сопрограмму? Мне нужно будет правильно изучить использование сопрограмм, но это имеет больший смысл. –

10

Я думаю Я понимаю, что функция aiter() вызывается асинхронно, так что каждая итерация aiter может протекать без предыдущего еще обязательно возвращение (или это понимание не так?).

Это понимание неправильное. Итерации цикла async for не могут выполняться параллельно. async for так же последовательно, как и обычный цикл for.

Асинхронная часть async for заключается в том, что она позволяет итератору await от имени сопроводителя. Он предназначен только для использования в асинхронных сопрограммах и только для использования на специальных асинхронных итерациях. Кроме этого, он в основном похож на обычный цикл for.

+0

Спасибо, мне кажется, мне нужно было пойти и правильно понять сопрограммы, прежде чем пытаться обернуть голову вокруг использования 'async'. Я ценю исправление. :) –