异步功能的可选同步接口

fre*_*bie 4 python asynchronous tornado synchronous

我正在编写一个使用Tornado Web tornado.httpclient.AsyncHTTPClient发出请求的库,该库为我的代码提供了以下async接口:

async def my_library_function():
    return await ...
Run Code Online (Sandbox Code Playgroud)

如果用户提供了kwarg,我想使此接口可选地为串行接口,例如:serial=True。尽管显然不能async从普通函数中调用用关键字定义的函数await。这将是理想的-尽管目前在该语言中几乎是不可能的:

async def here_we_go():
    result = await my_library_function()
    result = my_library_function(serial=True)
Run Code Online (Sandbox Code Playgroud)

我无法在网上找到任何东西,有人为此提出了一个不错的解决方案。我不想重新实现基本相同的代码,而不会出现awaits混乱。

这是可以解决的问题,还是需要语言的支持?


解决方案(尽管使用Jesse的-如下所述)

Jesse下面的解决方案几乎是我要使用的解决方案。我最终还是通过使用装饰器获得了我最初想要的界面。像这样:

import asyncio
from functools import wraps


def serializable(f):
    @wraps(f)
    def wrapper(*args, asynchronous=False, **kwargs):
        if asynchronous:
            return f(*args, **kwargs)
        else:
            # Get pythons current execution thread and use that
            loop = asyncio.get_event_loop()
            return loop.run_until_complete(f(*args, **kwargs))
    return wrapper
Run Code Online (Sandbox Code Playgroud)

这为您提供了此界面:

result = await my_library_function(asynchronous=True)
result = my_library_function(asynchronous=False)
Run Code Online (Sandbox Code Playgroud)

我理智地在python的异步邮件列表中检查了这一点,我很幸运地得到Guido的响应,因此他礼貌地将其拒绝了:

代码气味-能够异步和同步调用相同的函数非常令人惊讶。同时也违反了经验法则,即参数的值不应该影响返回类型。

很高兴知道,即使没有被认为是一个很好的接口也是可能的。Guido本质上提出了Jesse的答案,并在库中引入了包装功能作为辅助工具,而不是将其隐藏在装饰器中。

A. *_*vis 5

当您要同步调用此类函数时,请使用run_until_complete

asyncio.get_event_loop().run_until_complete(here_we_go())
Run Code Online (Sandbox Code Playgroud)

当然,如果您经常在代码中执行此操作,则应为该语句提供一个缩写,也许就是:

def sync(fn, *args, **kwargs):
    return asyncio.get_event_loop().run_until_complete(fn(*args, **kwargs))
Run Code Online (Sandbox Code Playgroud)

然后,您可以执行以下操作:

result = sync(here_we_go)
Run Code Online (Sandbox Code Playgroud)