Python

Pythonにおけるリフレクション

More than 3 years have passed since last update.

Pythonで動的にインスタンスを作成したり、メソッドを取得して呼び出したり・・・といった、リフレクションの方法についてまとめ。

動的なインスタンス生成

Pythonで文字列などから動的にインスタンスを作成したい際は以下のようにする。
※例として、例外クラス(Exception)のインスタンスを作成してみる。

mod = __import__("exceptions",fromlist=["Exception"])
class_def = getattr(mod,"Exception")
obj = class_def("message") # obj = Exception('message',)

先頭の__import__は、インポート宣言に該当する(上記の例ではfrom exceptions import Exceptionを行ったのと同じ意味になる。なお、exceptionsは明示的に読み込む必要はない。あくまで例として挙げている)。

このモジュールからgetattrにより該当クラスのクラス定義を取得し、最後はそれを利用してオブジェクトを作成する。

<参考>
How to dynamically load a Python class

メソッド定義の取得

メソッドも含めたクラスなどの定義の取得にはinspectを利用する。
inspect.getmembersによって定義を取得し、getattrでオブジェクトからの定義を取得、それを実行するという流れになる。
もちろん、メソッド名にあらかじめあたりがついていれば直接getattrを呼び出してよい。

upper_method = filter(lambda f: f[0] == "upper", inspect.getmembers("String",inspect.isroutine))[0]
invoker = getattr("abc",upper_method[0])
invoker() # 'ABC'

デコレーターがついているものだけ取りたいなー、というのは当然の希望だが、現在のところ非常に泥臭い方法しかないようだ(具体的には、ソースコードを読み込み@始まりのものを探す感じになる)。

プロパティの取得

Publicなプロパティ(メンバ変数)を全部取得したい、という場合は以下のようにする。

attributes = inspect.getmembers(self, lambda a: not(inspect.isroutine(a)))
filter(lambda a: not(a[0].startswith("__")), attributes)

isroutineに該当しないもの=メンバ変数を抽出し、その名前の先頭が__で始まらないもの、を抽出している。
取得したプロパティから値を取得するには、メソッド同様getattrを利用する。