该问题与synchronous version中的同一问题有关。 目标是设计装饰器,该装饰器将生成器或协程作为输入。我的代码如下:
import asyncio
def say_hello(function):
async def decorated(*args, **kwargs):
return_value = function(*args, **kwargs)
if isinstance(return_value, types.AsyncGeneratorType):
print("Hello async generator!")
async for v in return_value:
yield v
else:
print("Hello coroutine!")
return await return_value
return decorated
@helpers.say_hello
async def generator():
for i in range(5):
await asyncio.sleep(0.2)
yield i
@helpers.say_hello
async def coroutine():
await asyncio.sleep(1)
return list(range(5))
async def test():
for v in generator():
print(v)
for v in coroutine():
print(v)
给出的错误是:
'return' with value in async generator
我猜测这是根据decorated
包含yield
和return
以及值的事实进行静态检查的。
有什么办法可以使这项工作吗? (除了在say_hello
中使用参数来指定function
是生成器还是协程)。
答案 0 :(得分:1)
您基本上可以使用其他答案所部署的相同机制,但适用于协程。例如:
def say_hello(function):
def decorated(*args, **kwargs):
function_instance = function(*args, **kwargs)
if isinstance(function_instance, types.AsyncGeneratorType):
async def inner():
print("Hello async generator!")
async for v in function_instance:
yield v
else:
async def inner():
print("Hello coroutine!")
return await function_instance
return inner()
return decorated
请注意,在这种情况下,decorated
是使用def
而不是async def
定义的。这样可以确保协程对象上的异步生成器-迭代器在调用时立即开始运行并能够选择要返回的内容。由于decorated
返回一个通过调用用async def inner
定义的函数创建的对象,因此它在功能上等效于async def
本身。
您的test
函数不正确,因为它使用for
遍历异步生成器,并遍历协程。相反,它应该是async def
,并使用async for
遍历异步生成器,并使用await
等待协程。我使用以下代码进行测试(generator
和coroutine
不变):
async def test():
async for v in generator():
print(v)
await coroutine()
asyncio.run(test())
# or, on Python 3.6 and older:
#asyncio.get_event_loop().run_until_complete(test())