関数またはメソッドが通常の関数か非同期関数かを確認するにはどうすればよいですか?コードで通常または非同期のコールバックを自動的にサポートし、渡される関数のタイプをテストする方法が必要です。
_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()
の実装方法がわかりません。
Pythonの inspect モジュールを使用します。
inspect.iscoroutinefunction(object)
オブジェクトがコルーチン関数(async def構文で定義された関数)の場合、trueを返します。
この関数は、Python 3.5以降で使用できます。モジュールは、Python 2で使用できますが、機能が少なく、目的の関数がなくても使用できます。 検査
名前が示すようにモジュールを検査することは、多くのことを検査するのに役立ちます。ドキュメントは言う
Inspectモジュールは、モジュール、クラス、メソッド、関数、トレースバック、フレームオブジェクト、コードオブジェクトなどのライブオブジェクトに関する情報を取得するのに役立ついくつかの便利な関数を提供します。たとえば、クラスの内容を調べたり、メソッドのソースコードを取得したり、関数の引数リストを抽出してフォーマットしたり、詳細なトレースバックを表示するために必要なすべての情報を取得したりできます。
このモジュールが提供するサービスには、主に4つの種類があります。型チェック、ソースコードの取得、クラスと関数の検査、インタプリタスタックの検査です。
このモジュールのいくつかの基本的な機能は次のとおりです。
inspect.ismodule(object)
inspect.isclass(object)
inspect.ismethod(object)
inspect.isfunction(object)
また、ソースコードを取得する機能も搭載しています
inspect.getdoc(object)
inspect.getcomments(object)
inspect.getfile(object)
inspect.getmodule(object)
メソッドは直感的に名前が付けられます。必要に応じて説明をドキュメントに記載しています。
inspect
を使用して別のインポートを導入したくない場合は、iscoroutine
内でasyncio
も使用できます。
import asyncio
def isAsync(someFunc):
return asyncio.iscoroutinefunction(someFunc)
コルーチンにはCOROUTINE
フラグセット、コードフラグのビット7があります。
_>>> async def foo(): pass
>>> foo.__code__.co_flags & (1 << 7)
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__
_属性があることを確認するため)、そのフラグをテストします。 ソースコード を参照してください。
もちろん、inspect.iscoroutinefunction()
を使用するのが最も読みやすく、コードフラグが変更されても機能し続けることが保証されています。
_>>> inspect.iscoroutinefunction(foo)
True
_
[〜#〜] tldr [〜#〜]
何かをawait
で使用する必要があることを確認する場合は、_inspect.isawaitable
_を使用します(関数であるだけでなく、callable()
であるものをテストする場合と同様)。
iscoroutine
またはiscoroutinefunction
とは異なり、Future
sおよび___await__
_メソッドを実装するオブジェクトに対しても機能します。
詳細
上記のソリューションは、コルーチン関数を渡す場合、単純なケースで機能します。コルーチン関数のように機能するがコルーチン関数ではない待機可能なオブジェクト関数を渡したい場合があります。 2つの例は Future クラスまたはFuture-like objectclass(class that implements ___await__
_ magic method)です。この場合、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')
_
それはより普遍的な(そして私は論理的に正しいと確信しています)ソリューションです。