Да, существуют функциональные различия между нативными сопрограммами с использованием синтаксиса async def
и генераторов на основе генераторов с использованием декоратора asyncio.coroutine
.
Согласно PEP 492, который вводит синтаксис async def
:
Родные сопрограммная объекты не реализуют __iter__
и __next__
методы. Поэтому они не могут быть повторены или пройдены до iter()
, list()
, tuple()
и другие встроенные модули. Они также не могут использоваться в петле for..in
.
Попытка использования __iter__
или __next__
на нативном сопрограмме приведет к возникновению TypeError.
Простые генераторы не yield from
родной сопрограммы: выполнение так приведет к TypeError.
генератор на основе сопрограмма (для asyncio кода должна быть украшена @asyncio.coroutine
) может объекты yield from
родных сопрограммную.
inspect.isgenerator()
и inspect.isgeneratorfunction()
возвращение False
для родных сопрограмм объектов и нативных функций сопрограмм.
пункт 1 выше означает, что в то время как сопрограмма функции, определяемые с помощью синтаксиса @asyncio.coroutine
декоратор может вести себя как традиционные функции генератора, которые определены с помощью синтаксиса async def
не может.
Вот две минимальной, якобы эквивалентная сопрограмма функции, определенная с двумя синтаксисами:
import asyncio
@asyncio.coroutine
def decorated(x):
yield from x
async def native(x):
await x
Хотя байткод для этих двух функций практически идентичен:
>>> import dis
>>> dis.dis(decorated)
5 0 LOAD_FAST 0 (x)
3 GET_YIELD_FROM_ITER
4 LOAD_CONST 0 (None)
7 YIELD_FROM
8 POP_TOP
9 LOAD_CONST 0 (None)
12 RETURN_VALUE
>>> dis.dis(native)
8 0 LOAD_FAST 0 (x)
3 GET_AWAITABLE
4 LOAD_CONST 0 (None)
7 YIELD_FROM
8 POP_TOP
9 LOAD_CONST 0 (None)
12 RETURN_VALUE
... единственное отличие GET_YIELD_FROM_ITER
vs GET_AWAITABLE
, они ведут себя совершенно по-разному, когда предпринимаются попытки перебирать возвращаемые объекты:
>>> list(decorated('foo'))
['f', 'o', 'o']
>>> list(native('foo'))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'coroutine' object is not iterable
Очевидно 'foo'
не является awaitable, поэтому попытка вызвать native()
с ним не имеет смысла, но суть, надеюсь, понятно, что coroutine
объект возвращает не итерацию, независимо от его аргумента.
Более подробное исследование синтаксиса async
/await
от Brett Cannon: How the heck does async/await work in Python 3.5? охватывает эту разницу более подробно.
Было бы полезно знать, почему именно «никогда не использовать @coroutine» в 3.5+. Есть ли настоящая причина или это мнение/эмпирическое правило? Например, у меня есть некоторые декораторы @coroutine в 3.4-унаследованной кодовой базе, но вся новая разработка находится в 3.5. Должен ли я превращать эти декораторы в 'async def'? – hmijail
Чтобы приостановить выполнение сопрограммы, необходимо «уступить». Но 'yield' не допускается изнутри собственных сопрограмм! Так выглядит, что по-прежнему необходимо использовать '@ coroutine' даже в Python 3.5+ – lesnik
№. Просто используйте' await asyncio.sleep (0) '. –