【问题标题】:Turn a sync function into an awaitable function将同步函数变成等待函数
【发布时间】:2021-10-16 20:46:39
【问题描述】:

我有一种情况,我不能确定一个函数是同步的还是异步的。换句话说,该函数的类型签名为Union[Callable[..., Awaitable[Any]], Callable[..., Any]]

我似乎找不到将上述类型签名转换为Callable[..., Awaitable[Any]] 的一致类型的好方法(未弃用)。

我能够找到如何做到这一点的唯一方法是这样做:

import asyncio
import inspect
from typing import Any, Awaitable, Callable, List, Union

def force_awaitable(function: Union[Callable[..., Awaitable[Any]], Callable[..., Any]]) -> Callable[..., Awaitable[Any]]:
    if inspect.isawaitable(function):
        # Already awaitable
        return function
    else:
        # Make it awaitable
        return asyncio.coroutine(function)

但是,asyncio.coroutine(通常用作装饰器)自 Python 3.8 起已弃用。 https://docs.python.org/3/library/asyncio-task.html#asyncio.coroutine

这里提供的替代方法对我不起作用,因为我不使用 asyncio.coroutine 作为装饰器。与装饰器不同,async 关键字不能用作函数。

如何将同步函数(不可等待)转换为异步函数(可等待)?

其他考虑的选项

上面我已经表明,您可以检测是否有可等待的内容。这使我们可以像这样更改调用方式:

def my_function():
    pass

if inspect.isawaitable(function):
    await my_function()
else:
    my_function()

但是,这感觉很笨重,会创建混乱的代码,并在大循环中创建不必要的检查。我希望能够在进入循环之前定义如何调用该函数。


在 NodeJS 中,我只是等待同步功能:

// Note: Not Python!
function sync() {}
await sync();

当我尝试在 Python 中做同样的事情时,我遇到了一个错误:

def sync():
  pass
await sync() # AttributeError: 'method' object has no attribute '__await__'

【问题讨论】:

    标签: python asynchronous async-await


    【解决方案1】:

    您可以返回一个 async 函数,其作用域内包含原始 function 对象:

    def to_coroutine(f:Callable[..., Any]):
       async def wrapper(*args, **kwargs):
          return f(*args, **kwargs)
       return wrapper
    
    def force_awaitable(function: Union[Callable[..., Awaitable[Any]], Callable[..., Any]]) -> Callable[..., Awaitable[Any]]:
       if inspect.iscoroutinefunction(function):
          return function
       else:
          return to_coroutine(function)
    

    现在,如果function 不是awaitable,force_awaitable 将返回一个包含function 的协程函数。


    def test_sync(*args, **kwargs):
       pass
    
    async def main():
       await force_awaitable(test_sync)('foo', 'bar')
    
    asyncio.run(main())
    

    【讨论】:

    • 不幸的是,这无济于事。它会将函数转换为coroutine object 而不是coroutine function。这意味着你不能这样做:await force_awaitable(my_function)("bar", "baz") # TypeError: 'coroutine' object is not callable
    • @Lakitna 啊,我明白了,请看我最近的编辑。
    • 是的!哦,伙计,我找这个很久了。这正是我正在寻找的。谢谢!
    • @Lakitna 很高兴为您提供帮助!
    • @Lakitna 谢谢,请看我最近的编辑
    猜你喜欢
    • 2019-06-16
    • 1970-01-01
    • 1970-01-01
    • 2019-11-29
    • 2023-03-10
    • 2021-10-16
    • 2023-03-13
    • 2017-04-06
    • 1970-01-01
    相关资源
    最近更新 更多