【问题标题】:partial asynchronous functions are not detected as asynchronous部分异步函数未被检测为异步
【发布时间】:2018-09-20 10:22:08
【问题描述】:

我有一个函数同时接受常规和异步函数(不是协程,而是返回协程的函数)。

在内部它使用asyncio.iscoroutinefunction() test 来查看它获得了哪种类型的函数。

最近,当我尝试创建部分异步函数时,它崩溃了。

在这个演示中,ptest不被识别为协程函数,即使它返回一个协程,即ptest()是协程。

import asyncio
import functools

async def test(arg): pass
print(asyncio.iscoroutinefunction(test))    # True

ptest = functools.partial(test, None)
print(asyncio.iscoroutinefunction(ptest))   # False!!

print(asyncio.iscoroutine(ptest()))         # True

问题原因很清楚,但解决方法不明确。

如何动态创建通过测试的部分异步函数?

或

如何测试包裹在partial object 中的函数?

任何一个答案都可以解决问题。

【问题讨论】:

    标签: python python-asyncio


    【解决方案1】:

    使用 partial() 对象通过该测试,因为该测试要求将 __code__ 对象直接附加到您传递给 inspect.iscoroutinefunction() 的对象上。

    您应该改为测试 partial 包装的函数对象,可通过 partial.func attribute 访问:

    >>> asyncio.iscoroutinefunction(ptest.func)
    True
    

    如果您还需要测试partial() 对象,则针对functools.partial 进行测试:

    def iscoroutinefunction_or_partial(object):
        while isinstance(object, functools.partial):
            object = object.func
        return inspect.iscoroutinefunction(object)
    

    在 Python 3.8(和更高版本)中,inspect 模块(asyncio.iscoroutinefunction() 委托给)中的相关代码是 updated to handle partial() objects,您不再需要自己解开 partial() 对象。该实现使用相同的while isinstance(..., functools.partial) 循环。

    【讨论】:

    • 谢谢。 .func attr 是否记录在某处?
    • @VPfB:是的,functools.partial() documentation 声明它明确存在; 大致相当于代码显示了设置的属性。
    • functools 文档的链接有一些问题(2018 年),这就是我没有找到信息的原因:bugs.python.org/issue34748
    • @DurandA:你的确切问题是什么?如何测试 partialmethod 对象包装协程函数?然后它取决于包装的确切内容以及您如何访问 partialmethod 对象。如果您通过ClassObj.partialmethod_name 或instance.partialmethod_name 访问它,并且它包装了一个普通的async def 方法,则会发生描述符绑定并返回一个partial() 对象。所以在这种情况下,同样的技术也适用。
    • @DurandA:实际上,这并不完全正确。我提交了a new bug report,因为我发现这有点复杂。对于instance.partialmethod_name,您可以简单地展开first(您也必须在Python 3.8 中这样做)。对于ClassObj.partialmethod_name,您也必须查找_partialmethod 属性,遵循该属性,然后然后遵循.func 属性。
    【解决方案2】:

    我通过用async_partial 替换所有partial 实例解决了这个问题:

    def async_partial(f, *args):
       async def f2(*args2):
           result = f(*args, *args2)
           if asyncio.iscoroutinefunction(f):
               result = await result
           return result
    
       return f2
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-06
      • 2017-08-15
      • 2022-11-10
      • 2018-11-17
      • 1970-01-01
      • 2014-02-08
      相关资源
      最近更新 更多