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

时间:2016-03-18 05:13:05

标签: python python-3.x asynchronous python-3.5 coroutine

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

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)

根据传递的函数类型,它应该正常运行或者等待。我尝试了各种各样的事情,但不知道如何实现isAsync()

5 个答案:

答案 0 :(得分:31)

使用Python的inspect模块。

class BookAdmin(admin.ModelAdmin): ... prepopulated_fields = {'rent_price': ('calculated value based on mrp',),} ...

  

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

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

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

  

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

     

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

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

inspect.iscoroutinefunction(object)

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

inspect.ismodule(object)
inspect.isclass(object)
inspect.ismethod(object)
inspect.isfunction(object)

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

答案 1 :(得分:28)

如果您不想使用inspect引入其他导入,iscoroutine内也可以使用asyncio

import asyncio

def isAsync(someFunc):
    return asyncio.iscoroutinefunction(someFunc)

答案 2 :(得分:15)

协同例程设置了COROUTINE标志,代码标志中的第6位:

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

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

>>> import inspect
>>> inspect.CO_COROUTINE
128
>>> foo.__code__.co_flags & inspect.CO_COROUTINE
128

inspect.iscoroutinefunction() function正是这样做的;测试对象是否是函数或方法(以确保存在__code__属性)并测试该标志。请参阅source code

当然,使用inspect.iscoroutinefunction()是最具可读性的,并保证在代码标志发生变化时继续工作:

>>> inspect.iscoroutinefunction(foo)
True

答案 3 :(得分:9)

当你传递协程函数时,上面的解决方案适用于简单的情况。在某些情况下,您可能希望传递 awaitable object 函数,其功能类似于协程功能,但不是协程功能。两个示例是Future类或类似Future的对象类(implements __await__魔术方法的类)。在这种情况下,iscoroutinefunction将返回False,这不是您需要的。

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

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

await callCallback(SmartCallback)  # Should work, right?

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

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

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

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')

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

答案 4 :(得分:0)

扩展上面的答案。从 python 3.6 开始就有 4 types of functions :

  • 功能
  • 生成器函数
  • 协程函数
  • 异步生成器函数

如果您的应用程序事先不知道给定函数的类型,它可能是上述函数之一,异步函数可以是协程函数异步生成器函数< /强> 。 asyncio.iscoroutinefunction(someFunc) 只检查一个函数是否是协程函数,对于异步生成器,可以使用 inspect.isasyncgenfunction()。示例代码如下所示:

import inspect, asyncio

def isAsync(someFunc):
    is_async_gen = inspect.isasyncgenfunction(someFunc)
    is_coro_fn = asyncio.iscoroutinefunction(someFunc)
    return is_async_gen or is_coro_fn