为什么我们需要
asyncio.coroutine
装饰器?它提供什么功能?
例如:
# import asyncio
# @asyncio.coroutine
def gen():
value = yield("Started")
print(value)
a = gen()
a.send(None)
a.send("Done")
现在,如果我取消注释前两行并使用
asyncio.coroutine
装饰器,我仍然会得到相同的输出。
我的意思是这已经是一个
coroutine
- 一个可以暂停并通过参数传入的函数。为什么我需要用另一个 coroutine
来装饰它,即 asyncio.coroutine
?
重要的是要了解 生成器 和
asyncio
协程 - 是不同的东西。协程是使用生成器实现的,但(理论上)可以在没有生成器的情况下实现。生成器 - 是协程实现的一部分。
由于
asyncio
协程是使用生成器实现的,因此有时可以使用生成器作为协程而不会出现错误:
import asyncio
def main():
yield from asyncio.sleep(1)
print('done')
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
结果:
done
但它并不适用于所有类型的协程:
import asyncio
def main():
# yield from asyncio.sleep(1)
print('done')
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
结果:
TypeError: An asyncio.Future, a coroutine or an awaitable is required
这就是为什么(除了其他的东西)
asyncio.coroutine
装饰器使用:
import asyncio
@asyncio.coroutine
def main():
# yield from asyncio.sleep(1)
print('done')
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
结果:
done
但正如大家已经指出的那样,今天它实际上并不重要:因为 Python 3.5 装饰器和
yield from
已被关键字 async def
和 await
取代,这不仅更好,而且有助于将协程与其实现细节分开一个更好的方法。
import asyncio
async def main():
# await asyncio.sleep(1)
print('done')
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
我参加这个聚会超级晚了,但从历史的角度来看,我对这个装饰者所做的事情很感兴趣。
接受的答案根本就是错误的,或者实际上是不合逻辑的,编辑它将是整个重写,所以最好将其留下并单独回答。
对于 OP 示例,
@asyncio.coroutine
所做的是将 CO_ITERABLE_COROUTINE
标志附加到对象的 co_flags
。这使得生成器可以与 await
关键字一起使用,也可以用于 yield from
本身不是生成器派生的“纯”协程。
# Check if 'func' is a generator function.
# (0x20 == CO_GENERATOR)
if co_flags & 0x20:
# TODO: Implement this in C.
co = func.__code__
func.__code__ = CodeType(
co.co_argcount, co.co_posonlyargcount, co.co_kwonlyargcount, co.co_nlocals,
co.co_stacksize,
co.co_flags | 0x100, # 0x100 == CO_ITERABLE_COROUTINE
co.co_code,
co.co_consts, co.co_names, co.co_varnames, co.co_filename,
co.co_name, co.co_firstlineno, co.co_lnotab, co.co_freevars,
co.co_cellvars)
return func
装饰器还有一些其他用途,特别是(如已接受的答案中所示),如果它还不是生成器,它会将常规函数转换为协程。