使用带有装饰协同程序的单个事件循环返回将来的结果

时间:2016-10-28 13:46:29

标签: python python-3.5 python-asyncio

我有一个装饰器来装饰一个协同程序函数,并将协同程序返回的值分配给将来的实例。

import asyncio
import functools


def ensure_prepare(future):
    async def decorator(asyncfunc):
        @functools.wraps(asyncfunc)
        async def wrapper(*args, **kwargs):
            future.set_result(await asyncfunc(*args, **kwargs))
            return future
        return wrapper
    return decorator

演示:

>>> future = asyncio.Future()
>>> 
>>> @ensure_prepare(future)
... async def check_sanity():
...     return 9
... 
>>> loop = asyncio.get_event_loop()
>>> loop.run_until_complete(check_sanity)
<function check_sanity at 0x7f935300a158>
>>> _()
<coroutine object check_sanity at 0x7f934f78a728>
>>> loop.run_until_complete(_)
<Future finished result=9>
>>> _.result()
9

正如您所看到的,我需要运行两次事件循环才能获得未来的结果。有没有办法让事件循环在第一次运行后返回值?我不想在我的代码中等待并将结果(函数)分配给名称。

1 个答案:

答案 0 :(得分:1)

在您的代码中,您已将decorator包装器设为async这不是您想要的,这意味着每次使用包装器时它都会返回一个将生成包装的协同程序对象功能:

>>> future = asyncio.Future()
>>> @ensure_prepare(future)
async def chech_sanity():
    return 9

>>> check_sanity
<coroutine object ensure_prepare.<locals>.decorator at 0x10572f4c0>

>>> check_sanity.send(None) #advance coroutine
Traceback (most recent call last):
  File "<pyshell#7>", line 1, in <module>
    check_sanity.send(None) #advance coroutine
StopIteration: <function check_sanity at 0x105096a60>

               # ^ the function is the result of the coroutine

所以只需删除

行中的async即可
async def decorator(asyncfunc):

你的问题将得到解决:

def ensure_prepare(future):
    def decorator(asyncfunc):
        @functools.wraps(asyncfunc)
        async def wrapper(*args, **kwargs):
            future.set_result(await asyncfunc(*args, **kwargs))
            return future
        return wrapper
    return decorator


>>> future = asyncio.Future()
>>> @ensure_prepare(future)
async def check_sanity():
    return 9

>>> chech_sanity
<function check_sanity at 0x105784a60>
>>> loop = asyncio.get_event_loop()
>>> loop.run_until_complete(check_sanity()) #remember to call check_sanity!
<Future finished result=9>