随着我见过的
asyncio
图书馆,
@asyncio.coroutine
def function():
...
和
async def function():
...
可互换使用。
两者在功能上有什么区别吗?
是的,使用
async def
语法的本机协程和使用 asyncio.coroutine
装饰器的基于生成器的协程之间存在功能差异。
根据PEP 492,介绍了
async def
语法:
原生协程对象不实现
并且__iter__
方法。因此,它们不能被迭代或传递 到__next__
、iter()
、list()
和其他内置组件。他们也 不能在tuple()
循环中使用。for..in
尝试在本机协程上使用
或__iter__
对象将导致 TypeError 。__next__
普通生成器不能
原生协程:这样做 将导致 TypeError 。yield from
基于生成器的协程(对于异步代码,必须用
)可以@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
与 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
对象不是可迭代的,无论其参数如何。
Brett Cannon 对
async
/await
语法的更详细研究:Python 3.5 中的 async/await 到底是如何工作的? 更深入地涵盖了这种差异。
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
。
从 Python 3.5
coroutines
正式成为一种独特的类型,因此 async def
语法以及 await
语句。
在此之前,Python 3.4通过将常规函数包装到
generators
中来创建协程,因此使用了装饰器语法,以及更像生成器的yield from
。
在Python 3.4中,当原生协程不可用时,异步编程是使用
@asyncio.coroutine
实现的,它使用yield from
语法来暂停和恢复生成器以执行异步任务。
但是,在Python 3.5中,引入了原生协程的概念,它使用
async def
来定义协程,可以使用await
来暂停协程。这允许有效地暂停执行而不阻塞事件循环,从而提高性能。
因此,
async def
成为执行异步任务的新标准和首选方式。
这是一个简单的异步函数:
async def my_coroutine():
await fetch_db_record() # some asynchronous function