クラスとインスタンスの情報を取得する
【 目次 】
オブジェクトの情報を保持する特殊属性
クラスとクラスのインスタンスには特殊属性が定義されていて、そのオブジェクトの持っている情報を知る事ができる。
クラス(クラスオブジェクト)の特殊属性
- __name__
- クラス名
- __module__
- クラスが定義されているモジュールの名前
- __dict__
- クラスの名前空間が入った辞書
- _bases__
- 定義順の基底クラスのリスト(タプル)
- __doc__
- クラスのドキュメンテーション文字列。ドキュメンテーション文字列がない場合には None。
class Person(object): "sample class person" other_name=u"ヒト" def __init__(self,name,age): self.name=name self.age=age def method(self): pass print Person.__name__ print Person.__module__ print Person.__dict__ print Person.__bases__ print Person.__doc__
実行結果
Person __main__ {'__module__': '__main__', 'method': <function method at 0x0274D1F0>, 'other_name': u'\u30d2\u30c8', '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': 'sample class person', '__init__': <function __init__ at 0x0274D170>} (<type 'object'>,) sample class person
インスタンス(インスタンスオブジェクト)の特殊属性
- __dict__
- インスタンスオブジェクトの属性の辞書
- __class__
- インスタンスのクラス
person=Person("gudon", 99) print person.__dict__ print person.__class__
実行結果
{'age': 99, 'name': 'gudon'} <class '__main__.Person'>
ところで特殊属性には代入可能なものもあるようで、__class__属性に他のクラスを代入してみると
class MyCls(object): pass class OtherCls(object): def other_cls_method(self): print self.__class__.__name__ my_inst = MyCls() my_inst.__class__=OtherCls my_inst.other_cls_method()
実行結果
OtherCls
なんと、驚くべき事にインスタンスのクラスを別のクラスに変更する事ができてしまう。
こんな事できちゃって良いのかなぁ?
型についての組込み関数
型を調べる - type関数
組込み関数typeを使うと、オブジェクトの型を調べる事ができる。
クラスPersonとそのインスタンスの型を表示してみる。
print type(Person) print type(person)
実行結果
<type 'type'> <class '__main__.Person'>
Personクラスの型はtype、
そしてPersonクラスのインスタンスの型はクラス名であるPersonになる。
型を判定する - isinstance関数,issubclass関数
組込み関数isinstance を使って、オブジェクトがクラスのインスタンスかどうか調べる事ができる。 組込み関数issubclassを使って、クラスのあるクラスのサブクラスであるかどうか調べる事ができる。
print isinstance(person, Person) class Man(Person): pass print issubclass(Man, Person)
実行結果
True True
属性のリストを取得する
オブジェクトの属性の辞書を保持する特殊フィールド__dict__
特殊属性の項で述べたとおりオブジェクトの属性は、特殊フィールド__dict__により辞書として取得できる。
前掲の__dict__属性の表示結果をみればわかるとおり
クラス(クラスオブジェクト)の__dict__属性にはクラスに属するクラス変数やメソッドのオブジェクトが保存されており、
クラスのインスタンス(インスタンスオブジェクト)の__dict__属性にはインスタンス変数の辞書が保存されている。
インスタンスメソッドはインスタンスよりアクセスするメソッドであるが、個々のインスタンスに個別のメソッドオブジェクトが格納されているのではなく、同じクラスのインスタンスであれば、同じメソッドオブジェクトが実行されるようにクラスに保存される。
従って、インスタンスメソッドはその名前に反してクラスに属するオブジェクトという事になる。
組み込み関数dirとvars
組み込み関数dirとvarsについては前にも「名前,変数名,シンボル - 愚鈍人」で述べたが、これらを使ってオブジェクトの属性を調べる事もできる。。
vars関数はオブジェクトの__dict__属性の値を返すのに対して、dir関数はより多くの情報を探して返してくれる。
上記のPersonクラスのインスタンスpersonのdir関数とvars関数の出力結果を比べてみる。
print vars(person) print dir(person)
実行結果
{'age': 99, 'name': 'gudon'} ['__doc__', '__init__', '__module__', 'age', 'method', 'name']
vars関数もdir関数も引数で指定したオブジェクトの属性を返すがその違いは
var関数が戻り値として辞書を返すのに対してdir関数はリストを返す事。
そしてvars関数の結果にはインスタンスメソッドmethodがメンバーには含まれていない。
あくまでも、インスタンスpersonに直接属する属性のみが含まれている。
これに対してdir関数の場合は、インスタンスの属するクラスの属性およびそのクラスの基底クラスの属性を再帰的にさぐってインスタンスメソッドなどのインスタンスよりアクセスされるクラスの属性なども探してくれるようだ。
クラスに特殊メソッド__dir__が定義されている場合、dir関数は特殊メソッド__dir__の結果をリストとして返す。
class MyCls(object): def __dir__(self): print "__dir__" return ["xxx","yyy"] my_inst=MyCls() print dir(my_inst)
実行結果
__dir__ ['xxx', 'yyy']
getmembersとpprint
標準モジュールinspectのgetmembers関数を使うとより多くの情報を取得できる。
また、標準モジュールpprintのpprint関数を使うとオブジェクトのデータ構造をフォーマットされた見やすい形で出力できる。
pprintについては
- ライブラリ:pprint - Life with Python
- pprint – データ構造を見やすい形で出力する - Python Module of the Week
- 8.18. pprint — データ出力の整然化 — Python 2.7ja1 documentation
inspectについては
- ライブラリ:inspect - Life with Python
- 27.13. inspect — 活動中のオブジェクトの情報を取得する — Python 2.7ja1 documentation
- inspect – Inspect live objects - Python Module of the Week
以下のコードはprint文とpprintを使ってgetmembers関数の結果を表示する例である。
print "----------------------- print inspect.getmembers(person)" print inspect.getmembers(person) print "----------------------- pprint.pprint(inspect.getmembers(person)" pprint.pprint(inspect.getmembers(person))
実行結果
----------------------- print inspect.getmembers(person) [('__class__', <class '__main__.Person'>), ('__delattr__', <method-wrapper '__delattr__' of Person object at 0x0281E030>), ('__dict__', {'age': 99, 'name': 'gudon'}), ('__doc__', 'sample class person'), ('__format__', <built-in method __format__ of Person object at 0x0281E030>), ('__getattribute__', <method-wrapper '__getattribute__' of Person object at 0x0281E030>), ('__hash__', <method-wrapper '__hash__' of Person object at 0x0281E030>), ('__init__', <bound method Person.__init__ of <__main__.Person object at 0x0281E030>>), ('__module__', '__main__'), ('__new__', <built-in method __new__ of type object at 0x1E2286B8>), ('__reduce__', <built-in method __reduce__ of Person object at 0x0281E030>), ('__reduce_ex__', <built-in method __reduce_ex__ of Person object at 0x0281E030>), ('__repr__', <method-wrapper '__repr__' of Person object at 0x0281E030>), ('__setattr__', <method-wrapper '__setattr__' of Person object at 0x0281E030>), ('__sizeof__', <built-in method __sizeof__ of Person object at 0x0281E030>), ('__str__', <method-wrapper '__str__' of Person object at 0x0281E030>), ('__subclasshook__', <built-in method __subclasshook__ of type object at 0x028203E0>), ('__weakref__', None), ('age', 99), ('method', <bound method Person.method of <__main__.Person object at 0x0281E030>>), ('name', 'gudon'), ('other_name', u'\u30d2\u30c8')] ----------------------- pprint.pprint(inspect.getmembers(person) [('__class__', <class '__main__.Person'>), ('__delattr__', <method-wrapper '__delattr__' of Person object at 0x0281E030>), ('__dict__', {'age': 99, 'name': 'gudon'}), ('__doc__', 'sample class person'), ('__format__', <built-in method __format__ of Person object at 0x0281E030>), ('__getattribute__', <method-wrapper '__getattribute__' of Person object at 0x0281E030>), ('__hash__', <method-wrapper '__hash__' of Person object at 0x0281E030>), ('__init__', <bound method Person.__init__ of <__main__.Person object at 0x0281E030>>), ('__module__', '__main__'), ('__new__', <built-in method __new__ of type object at 0x1E2286B8>), ('__reduce__', <built-in method __reduce__ of Person object at 0x0281E030>), ('__reduce_ex__', <built-in method __reduce_ex__ of Person object at 0x0281E030>), ('__repr__', <method-wrapper '__repr__' of Person object at 0x0281E030>), ('__setattr__', <method-wrapper '__setattr__' of Person object at 0x0281E030>), ('__sizeof__', <built-in method __sizeof__ of Person object at 0x0281E030>), ('__str__', <method-wrapper '__str__' of Person object at 0x0281E030>), ('__subclasshook__', <built-in method __subclasshook__ of type object at 0x028203E0>), ('__weakref__', None), ('age', 99), ('method', <bound method Person.method of <__main__.Person object at 0x0281E030>>), ('name', 'gudon'), ('other_name', u'\u30d2\u30c8')]
pprint関数の表示結果の方が見やすく表示されているのがわかる。
pyclbr - ソースコードを解析してクラスの情報を取得
標準モジュールpyclbrを使ってソースコードを解析してクラスやメソッド、関数についての情報を取得する事ができる。
- 31.9. pyclbr — Python クラスブラウザサポート — Python 2.7ja1 documentation
- pyclbr – Python クラスブラウザサポート - Python Module of the Week
pyclbrにはreadmodule関数とreadmodule_ex関数の2つの関数が用意されていてreadmodule関数の方がクラスの情報のみを取得するのに対して、readmodule_exの方は関数についての情報も取得してくれる。
ソースコードのファイル名を引数に指定してこれらの関数を実行すると、
ソースコードに含まれるクラスの情報をpyclbr.Class型のオブジェクトとして,関数の情報をpyclbr.Function型のオブジェクトとして、
クラス名,関数名をキーとする辞書オブジェクトに返してくれる。
以下のようにクラスと関数が定義されているpythonのソースコードを用意して、
sample_source.py
class Person(object): def __init__(self,name,age): self.name=name self.age=age def method(self): pass person=Person("gudon",99) def func(arg): pass
このソースをpyclbrを使って解析する。
import pyclbr import pprint obj_dict = pyclbr.readmodule('sample_source')
readmodule関数の戻り値をprint文で表示してみる。
print obj_dict
実行結果
{'Person': <pyclbr.Class instance at 0x02377E90>}
sample_source.pyのPersonクラスの情報がpyclbr.Classクラスのインスタンスに格納されているのがわかる。
同様に、readmodule_ex関数を使って関数の情報も取得してみると
obj_dict = pyclbr.readmodule_ex('sample_source') pprint.pprint(obj_dict)
実行結果
{'Person': <pyclbr.Class instance at 0x02753328>, 'func': <pyclbr.Function instance at 0x02753468>}
今度は関数の情報もpyclbr.Functionクラスのインスタンスとして辞書に追加されているのがわかる。
それではpyclbr.Classとpyclbr.Functionのインスタンスには何が格納されているのだろうか、vars関数を使って調べてみよう。
obj_dict = pyclbr.readmodule_ex('sample_source') for obj_name,obj_value in obj_dict.iteritems(): print obj_name pprint.pprint(vars(obj_value))
実行結果
Person {'file': 'ソースコードのpath\\sample_source.py', 'lineno': 3, 'methods': {'__init__': 5, 'method': 9}, 'module': 'sample_source', 'name': 'Person', 'super': ['object']} func {'file': 'ソースコードのpath\\sample_source.py', 'lineno': 13, 'module': 'sample_source', 'name': 'func'}
クラスや関数ノースコードの位置(行番号)やクラスに含まれるメソッド名と行番号のリストなどが格納されているのがわかる。
注意が必要なのは、pyclbr.Classのsuper属性はスパークラスのリストをClassオブジェクトのリストとして返すがpyclbrが見つけられなかったクラスは、 Class オブジェクトではなくクラス名の文字列としてリストに挙げられる。
これを加味するとともに、クラスや関数を名前順にソードして、表示させてみる。
import pyclbr import pprint obj_dict = pyclbr.readmodule_ex('sample_source') for obj_name, obj_value in sorted(obj_dict.iteritems()): print obj_name pprint.pprint(vars(obj_value)) if hasattr(obj_value, "super"): print [sobj_name if isinstance(sobj_name, basestring) else sobj_name.name for sobj_name in obj_value.super]