Rozwiązania powyżej będą działać w prostych przypadkach, gdy przejdziesz funkcję coroutine. W niektórych przypadkach możesz przesłać niezaprzeczalną funkcję obiektu, która działa jak funkcja coroutine, ale nie jest funkcją coroutine. Dwa przykłady to klasa Future lub Przyszłościowa klasa obiektu (klasa, która jest metodą magiczną). W takich przypadkach iscoroutinefunction
zwróci False
, czego nie potrzebujesz.
Łatwiej zrozumieć na non-asynchronicznym przykład przy przejściu zakaz funkcję wpłacone jako callback:
class SmartCallback:
def __init__(self):
print('SmartCallback is not function, but can be used as function')
await callCallback(SmartCallback) # Should work, right?
Powrót do asynchronicznej świecie, podobna sytuacja:
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
sposobem rozwiązania go nie używać iscoroutine
lub iscoroutinefunction
, ale zamiast tego użyj inspect.isawaitable
. Działa z gotowym obiektem, więc musisz go najpierw utworzyć. Innymi słowy, rozwiązanie, które powinienem zastosować:
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')
To bardziej uniwersalne (i jestem pewien, logicznie poprawne) rozwiązanie.
Ale ... ale "True/False" dzieli się na "0"! : O – Shadow