クラスとインスタンスの情報を取得する

【 目次 】

オブジェクトの情報を保持する特殊属性

クラスとクラスのインスタンスには特殊属性が定義されていて、そのオブジェクトの持っている情報を知る事ができる。

クラス(クラスオブジェクト)の特殊属性

__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については

inspectについては

以下のコードは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を使ってソースコードを解析してクラスやメソッド、関数についての情報を取得する事ができる。

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]
ページのトップへ戻る