如何判断函数或方法是正常函数还是异步函数?我希望我的代码能够自动支持普通或异步回调,并且需要一种方法来测试传递的函数类型。
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()
。
答案 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
解决问题的方法是不使用iscoroutine
或iscoroutinefunction
,而是使用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