使用我见过的asyncio
库,
@asyncio.coroutine
def function():
...
和
async def function():
...
可互换使用。
两者之间是否有任何功能差异?
答案 0 :(得分:42)
是的,使用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'
不是等待的,所以尝试用它来调用native()
并没有多大意义,但有希望明确的是coroutine
反对它无论其参数如何,return都不可迭代。
Brett Cannon对async
/ await
语法的更详细调查:How the heck does async/await work in Python 3.5?更深入地涵盖了这种差异。
答案 1 :(得分:14)
async def
是Python 3.5的新语法。
您可以在await
内使用async with
,async for
和async def
。
@coroutine
是async def
的功能类似物,但它适用于Python 3.4+并使用yield from
构造而不是await
。
对于实际观点,如果您的Python是3.5 +,则永远不要使用@coroutine
。
答案 2 :(得分:3)
来自 Python 3.5 coroutines
正式成为一种独特的类型,因此async def
语法以及await
语句。
在此之前, Python 3.4 通过将常规函数包装到generators
中来创建协同程序,因此装饰器语法和类似生成器的yield from
。