Python 测试函数或方法是正常的还是异步的
如何确定函数或方法是普通函数还是异步函数?我希望我的代码能够自动支持正常或异步回调,并且需要一种方法来测试传递的函数类型Python 测试函数或方法是正常的还是异步的,python,python-3.x,asynchronous,python-3.5,coroutine,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
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)
根据传递的函数类型,它应该正常运行,或者使用wait。我尝试了各种方法,但不知道如何使用Python模块实现isAsync()
inspect.iscoroutinefunction(对象)
如果对象是协程函数(使用异步定义语法定义的函数),则返回true
此函数从Python 3.5开始就可用。
该模块适用于Python 2,功能较少,当然没有您想要的模块:
顾名思义,Inspect模块对于检查很多事情都很有用。文件上说
inspect模块提供了几个有用的函数来帮助获取有关活动对象的信息,例如模块、类、方法、函数、回溯、帧对象和代码对象。例如,它可以帮助您检查类的内容、检索方法的源代码、提取函数的参数列表并设置其格式,或者获取显示详细回溯所需的所有信息
该模块提供四种主要服务:类型检查、获取源代码、检查类和函数以及检查解释器堆栈
本模块的一些基本功能包括:
inspect.ismodule(object)
inspect.isclass(object)
inspect.ismethod(object)
inspect.isfunction(object)
它还具有检索源代码的功能
inspect.getdoc(object)
inspect.getcomments(object)
inspect.getfile(object)
inspect.getmodule(object)
方法的命名直观。如有需要,可在文档中找到说明。协同例程设置了协同程序
标志,代码标志中的第7位:
>>> async def foo(): pass
>>> foo.__code__.co_flags & (1 << 7)
128 # not 0, so the flag is set.
政府正是这样做的;测试对象是否是函数或方法(以确保存在\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。看
当然,使用inspect.iscoroutinefunction()
是最具可读性的,并且保证在代码标志发生更改时继续工作:
>>> inspect.iscoroutinefunction(foo)
True
如果您不想使用inspect
引入另一个导入,那么iscoroutine
也可在asyncio
中使用
import asyncio
def isAsync(someFunc):
return asyncio.iscoroutinefunction(someFunc)
TLDR
如果您想检查应该与wait
一起使用的内容,请使用inspect.isawaitable
(就像您测试的内容是callable()
而不仅仅是一个函数)
与iscoroutine
或iscoroutinefunction
不同,它还适用于未来的
s和实现\uuuu wait\uuu
方法的对象
详细信息
当您传递协程函数时,上述解决方案将适用于简单情况。在某些情况下,您可能希望传递可等待的对象函数,该函数的行为类似于协程函数,但不是协程函数。两个例子是类或类似未来的对象类(类\uuuuu wait\uuuu
magic方法)。在这种情况下,iscoroutinefunction
将返回False
,这是您不需要的
在非异步示例中,将非可调用函数作为回调传递更容易理解:
class SmartCallback:
def __init__(self):
print('SmartCallback is not function, but can be used as function')
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')
这是一个更通用的解决方案(我相信逻辑上是正确的)。但是如果一个普通函数作为回调传递,恰好返回一个可等待的对象,那么这个行为不会改变吗?在这种情况下,返回的对象也会被等待/执行。类似于def testcb():返回asynchSmartCallback
@Ecko,以防您通过此testcb
,您得到的结果应该是asynchSmartCallback
,不会等待任何结果。之所以发生这种情况,是因为AsyncSmartCallback
不是可等待的对象,而是返回可等待对象的类:AsyncSmartCallback()
-这是可等待的对象。如果函数def testcb():return asynchSmartCallback()
,则将等待此对象。但是我看不出有什么问题:假设您传递def testcb():return Callback()
——在这种情况下,也会执行Callback()
。我喜欢最后的可调用选项。第二个也可以写在一个函数中,作为def\uuuuu wait\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuself:return(从asyncio.sleep(1)中产生)。\uuuuu wait\uuuuuuuuuuuuuuuuuu。假设OP理解它,不会通过长时间运行的同步回调。但是。。。但是True/False
除以0
!:OIs在inspect.iscoroutinefunction()
和asyncio.iscoroutinefunction()
?@M.I.Wright之间有实际区别吗?不,没有。基于Python 3.6的源代码,asyncio
版本只是从inspect
模块重新导入的一个版本。@AlesTeska我没有查看源代码,但py3.7文档有一个说明,表明它们有细微的不同,特别是:此方法不同于inspect.iscoroutinefunction()因为对于以@coroutine修饰的基于生成器的协程函数,它返回True。
好答案-两个建议:1)提及inspect.isawaitable
,就像其他答案中的一个一样-它检查稍有不同的事情,但涵盖某些其他情况,并注意权衡会得到更全面的答案,2)关于Python2后端端口的旁白可能应该扩展为注意Python2中甚至不存在async
/await
,或者完全忽略。@hallo它们不相同,请参见:与inspect.iscorou不同
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')