С 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 fromnative coroutines: это приведет к типу TypeError.основанные на генераторе сопрограммы (для асинхронного кода должны быть украшены
@asyncio.coroutine) могут@asyncio.coroutineyield 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 генератора.