Решения, указанные выше, будут работать в простых случаях, когда вы передадите функцию сопрограммы. В некоторых случаях вам может понравиться awaitable object функция, которая действует как функция coroutine, но не является функцией coroutine. Два примера: Future класс или Будущий объект класс (класс implements__await__
магия метод). В этом случае iscoroutinefunction
вернет False
, что вам не нужно.
Это легче понять на не асинхронном пример с прохождением не-функции вызываемым в качестве обратного вызова:
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')
Это более универсальный (и я уверен, что логически правильное) решение.
Но ... но 'True/False' делится на' 0'! : O – Shadow