С asyncio
библиотекой, которую я видел,
@asyncio.coroutine
def function():
...
а также
async def function():
...
используется взаимозаменяемо.
Есть ли функциональная разница между этими двумя?
С asyncio
библиотекой, которую я видел,
@asyncio.coroutine
def function():
...
а также
async def function():
...
используется взаимозаменяемо.
Есть ли функциональная разница между этими двумя?
Да, существуют функциональные различия между нативными сопрограммами, использующими синтаксис asyncio.coroutine
async def
и asyncio.coroutine
основе генератора, используя декоратор asyncio.coroutine
.
Согласно PEP 492, который вводит синтаксис async def
:
Собственные объекты coroutine не реализуют
__iter__
и__next__
. Следовательно, они не могут быть повторены или переданы вiter()
,list()
,tuple()
и другие встроенные модули. Они также не могут использоваться в циклеfor..in
.Попытка использовать
__iter__
или__next__
на нативном сопрограмме coroutine приведет к типу TypeError.Обычные генераторы не могут
yield from
native coroutines: это приведет к типу TypeError.основанные на генераторе сопрограммы (для асинхронного кода должны быть украшены
@asyncio.coroutine
) могут@asyncio.coroutine
yield from
собственных сопроводительных объектов.
inspect.isgenerator()
иinspect.isgeneratorfunction()
возвращаетFalse
для собственных объектов coroutine и собственных функций coroutine.
Пункт 1 выше означает, что в то время как функции coroutine, определенные с помощью синтаксиса @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'
не является ожидаемым, поэтому попытка вызвать native()
с ним не имеет большого смысла, но, надеюсь, ясно, что возвращаемый объект coroutine
не является итерируемым, независимо от его аргумента.
Более подробное исследование синтаксиса async
/await
Бретта Кэннона: Как работает асинхронный/ждущий процесс в Python 3.5? охватывает эту разницу более подробно.
async def
- новый синтаксис из Python 3.5. Вы можете использовать await
, async with
и async for
внутри async def
s.
@coroutine
является функциональным аналогом для async def
но он работает в Python 3. 4+ и использует yield from
строительства вместо await
.
Для практической перспективы просто никогда не используйте @coroutine
если ваш Python равен 3. 5+.
Из Python 3.5 coroutines
формально стал отдельным типом и, следовательно, синтаксисом async def
, а также await
.
До этого Python 3.4 создавал сопрограммы, обертывая регулярные функции в generators
, отсюда и синтаксис декоратора, и больше yield from
генератора.