问题描述
有了我见过的asyncio
库,
@asyncio.coroutine
def function():
...
和
async def function():
...
可互换使用.
两者之间在功能上有什么区别吗?
Is there any functional difference between the two?
推荐答案
是的,使用async def
语法的本地协程和使用asyncio.coroutine
装饰器的基于生成程序的协程之间在功能上存在差异.
Yes, there are functional differences between native coroutines using async def
syntax and generator-based coroutines using the asyncio.coroutine
decorator.
根据 PEP 492 ,其中介绍了async def
语法:
According to PEP 492, which introduces the async def
syntax:
-
本地协程对象未实现
__iter__
和__next__
方法.因此,它们不能被迭代或传递 到iter()
,list()
,tuple()
和其他内置文件.他们也 不能在for..in
循环中使用.
Native coroutine objects do not implement
__iter__
and__next__
methods. Therefore, they cannot be iterated over or passed toiter()
,list()
,tuple()
and other built-ins. They also cannot be used in afor..in
loop.
尝试在本机协程上使用__iter__
或__next__
对象将导致TypeError.
An attempt to use __iter__
or __next__
on a native coroutine object will result in a TypeError .
普通生成器无法yield from
本地协程: 会导致TypeError.
Plain generators cannot yield from
native coroutines: doing so will result in a TypeError .
基于发电机的协程(因为异步代码必须用 @asyncio.coroutine
)可以yield from
原生协程对象.
generator-based coroutines (for asyncio code must be decorated with @asyncio.coroutine
) can yield from
native coroutine objects.
inspect.isgenerator()
和inspect.isgeneratorfunction()
对于原生协程对象和原生协程函数返回False
.
上面的
第1点意味着,虽然使用@asyncio.coroutine
装饰器语法定义的协程函数可以像传统的生成器函数那样工作,但使用async def
语法定义的协程函数不能.
Point 1 above means that while coroutine functions defined using the @asyncio.coroutine
decorator syntax can behave as traditional generator functions, those defined with the async def
syntax cannot.
以下是用两种语法定义的两个最小的,表面上等效的协程函数:
Here are two minimal, ostensibly equivalent coroutine functions defined with the two syntaxes:
import asyncio
@asyncio.coroutine
def decorated(x):
yield from x
async def native(x):
await x
尽管这两个函数的字节码几乎相同:
Although the bytecode for these two functions is almost identical:
>>> 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
与GET_AWAITABLE
,当尝试遍历返回的对象时,它们的行为完全不同:
... the only difference being GET_YIELD_FROM_ITER
vs GET_AWAITABLE
, they behave completely differently when an attempt is made to iterate over the objects they return:
>>> 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
对象是不可迭代的,无论其如何论点.
Obviously 'foo'
is not an awaitable, so the attempt to call native()
with it doesn't make much sense, but the point is hopefully clear that the coroutine
object it returns is not iterable, regardless of its argument.
布雷特·坎农(Brett Cannon)对async
/await
语法的更详细研究:异步/等待在Python 3.5中如何工作?涵盖了更深的差异.
A more detailed investigation of the async
/await
syntax by Brett Cannon: How the heck does async/await work in Python 3.5? covers this difference in more depth.
这篇关于@ asyncio.coroutine与异步def的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!