测试函数或方法是正常还是异步

Eck*_*cko 30 python asynchronous coroutine python-3.x python-3.5

如何确定函数或方法是正常函数还是异步函数?我希望我的代码能够自动支持普通或异步回调,并且需要一种方法来测试传递的函数类型.

async def exampleAsyncCb():
    pass

def exampleNomralCb():
    pass

def isAsync(someFunc):
    #do cool dynamic python stuff on the function
    return True/False

async def callCallback(cb, arg):
    if isAsync(cb):
        await cb(arg)
    else:
        cb(arg)
Run Code Online (Sandbox Code Playgroud)

根据传递的函数类型,它应该正常运行或使用await运行.我尝试了各种各样的东西,但不知道如何实现isAsync().

Sha*_*rad 35

使用Python 的inspect模块.

inspect.iscoroutinefunction(object)

如果对象是协程函数(使用async def语法定义的函数),则返回true.

从Python 3.5开始,此功能可用.该模块适用于Python 2,具有较少的功能,当然没有您正在寻找的模块:inspect

检查模块顾名思义对检查很多东西很有用.文件说

inspect模块提供了几个有用的函数来帮助获取有关活动对象的信息,例如模块,类,方法,函数,回溯,框架对象和代码对象.例如,它可以帮助您检查类的内容,检索方法的源代码,提取和格式化函数的参数列表,或获取显示详细回溯所需的所有信息.

这个模块提供了四种主要的服务:类型检查,获取源代码,检查类和函数,以及检查解释器堆栈.

该模块的一些基本功能是:

inspect.ismodule(object)
inspect.isclass(object)
inspect.ismethod(object)
inspect.isfunction(object)
Run Code Online (Sandbox Code Playgroud)

它还具有检索源代码的功能

inspect.getdoc(object)
inspect.getcomments(object)
inspect.getfile(object) 
inspect.getmodule(object)
Run Code Online (Sandbox Code Playgroud)

方法直观地命名.如果需要,可以在文档中找到描述.

  • @AlesTeska我没有看过源代码,但是py3.7文档进行了澄清,表明它们有一些细微的不同,特别是:`此方法与inspect.iscoroutinefunction()不同,因为对于基于生成器的协程函数,它返回True用@coroutine装饰。 (4认同)
  • `inspect.iscoroutinefunction()` 和 `asyncio.iscoroutinefunction()` 之间有实际区别吗? (3认同)
  • @MIWright - 不,不是。基于 Python 3.6 的源代码,`asyncio` 版本​​只是从 `inspect` 模块重新导入。 (2认同)
  • 很好的答案 - 两个建议:1)提到“inspect.isawaitable”,就像其他答案之一一样 - 它检查略有不同的事情,但涵盖某些其他情况,并注意权衡将有助于获得更全面的答案,2)关于 Python 2 向后移植的旁白可能应该扩展以注意到 Python 2 中甚至不存在“async”/“await”,或者完全忽略。 (2认同)

dir*_*irn 30

如果你不想引入另一个导入inspect,iscoroutine也可以在里面asyncio.

import asyncio

def isAsync(someFunc):
    return asyncio.iscoroutinefunction(someFunc)
Run Code Online (Sandbox Code Playgroud)

  • `asyncio.iscoroutinefunction()`函数执行*two*tests; 它首先使用`inspect.iscoroutinefunction()`,如果该测试失败则测试该函数是否为[`@ acyncio.coroutine`装饰器]的函数(https://docs.python.org/3/library/asyncio -task.html #asyncio.coroutine)已应用.考虑到这一点! (9认同)

Mar*_*ers 19

协同例程具有COROUTINE标志集,代码标志中的位6:

>>> async def foo(): pass
>>> foo.__code__.co_flags & (2 << 6)
128   # not 0, so the flag is set.
Run Code Online (Sandbox Code Playgroud)

值128作为常量存储在inspect模块中:

>>> import inspect
>>> inspect.CO_COROUTINE
128
>>> foo.__code__.co_flags & inspect.CO_COROUTINE
128
Run Code Online (Sandbox Code Playgroud)

这个inspect.iscoroutinefunction()功能就是这样; 测试对象是否是函数或方法(以确保存在__code__属性)并测试该标志.查看源代码.

当然,inspect.iscoroutinefunction()如果代码标志要改变,使用是最可读的并保证继续工作:

>>> inspect.iscoroutinefunction(foo)
True
Run Code Online (Sandbox Code Playgroud)


Mik*_*mov 13

当你传递协程函数时,上面的解决方案适用于简单的情况.在某些情况下,您可能希望传递一个像协程函数一样的等待对象函数,但不是协程函数.两个例子是Future类或类似Future的对象类(实现 await魔术方法的类).在这种情况下inspect.isawaitable将返回callable(),你不需要什么.

通过将非函数callable作为回调传递,可以更容易理解非异步示例:

class SmartCallback:
    def __init__(self):
        print('SmartCallback is not function, but can be used as function')

await callCallback(SmartCallback)  # Should work, right?
Run Code Online (Sandbox Code Playgroud)

回到异步世界,类似的情况:

class AsyncSmartCallback:
    def __await__(self):
        return self._coro().__await__()

    async def _coro(self):
        print('AsyncSmartCallback is not coroutine function, but can be used as coroutine function')
        await asyncio.sleep(1)

await callCallback(AsyncSmartCallback)  # Should work, but oops! iscoroutinefunction(AsyncSmartCallback) == False
Run Code Online (Sandbox Code Playgroud)

解决它的方法不使用iscoroutineiscoroutinefunction,而是使用Future.它适用于就绪对象,因此您必须先创建它.换句话说,我建议使用的解决方案:

async def callCallback(cb, arg):
    if callable(cb):
        res = cb()  # here's result of regular func or awaitable
        if inspect.isawaitable(res):
            res = await res  # await if awaitable
        return res  # return final result
    else:
        raise ValueError('cb is not callable')
Run Code Online (Sandbox Code Playgroud)

它更通用(我确信在逻辑上是正确的)解决方案.

  • @makeroo 一开始我很困惑,一切都是正确的:“f”不是可等待的,但“f()”是。`inspect.iswaitable(f()) True` (3认同)
  • 但是,如果将普通函数作为回调传递而恰好返回一个等待对象,那不会改变行为,在这种情况下,返回的对象也将被等待/执行。就像`def testcb():return AsyncSmartCallback` (2认同)
  • @Ecko,以防您通过此`testcb`,结果将是应有的`AsyncSmartCallback`,不会等待任何事情。发生这种情况是因为AsyncSmartCallback不是等待对象,它是返回等待对象的类:AsyncSmartCallback()-这是等待对象。如果您的函数`def testcb():return AsyncSmartCallback()`,则将等待此对象。但是我没有发现任何问题:假设您通过了`def testcb():return Callback()`-在这种情况下,也会执行`Callback()`。 (2认同)
  • 至于python 3.9,inspect.isawaitable无法识别异步def函数:``` &gt;&gt;&gt; async def f(): pass ... &gt;&gt;&gt; inspect.isawaitable(f) False ``` (2认同)