初めてのPython(26章前編)
属性検索順制御
スーパークラスとして、ビルドインオブジェクト/objectを指定したクラスのインスタンスでは、
属性の検索が幅優先で行われる。
それだと困る(深さ優先にして欲しい)場合は、優先したい属性をクラスの中で明示的に指定する。
[kobakoba0723@fedora13-intel64 ~]$ cat new-style.py #!/usr/bin/env python class A(object): def __init__(self): self.attr = 1 class B(A): pass class C(A): def __init__(self): self.attr = 2 class D(B, C): def __init__(self): A.__init__(self) class E(B, C): def __init__(self): C.__init__(self) if __name__ == '__main__': x = D() y = E() print x.attr, y.attr [kobakoba0723@fedora13-intel64 ~]$ python new-style.py 1 2 [kobakoba0723@fedora13-intel64 ~]$
属性として変数を使ったけど、メソッドの場合も同じことようにすれば検索順を制御できる。
__slots__
クラス属性としてインスタンス属性として使いたい変数名のシーケンスを渡すと、
その変数名以外のインスタンスを作れなく出来る。
[kobakoba0723@fedora13-intel64 ~]$ python Python 2.6.4 (r264:75706, Apr 1 2010, 02:55:51) [GCC 4.4.3 20100226 (Red Hat 4.4.3-8)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> class A(object): ... __slots__ = ['hoge', 'piyo'] ... >>> x = A() >>> x.hoge ='hoge' >>> x.spam ='spam' Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'A' object has no attribute 'spam' >>> dir(x) ['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'hoge', 'piyo'] >>>
ん?、piyoっていうインスタンス属性が作られている。。。
>>> x.piyo Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: piyo >>> x.piyo = 'piyo' >>>
しかも属性エラーが出た。変数としては存在する(?)のに、アクセスできない??
Pythonのドキュメントの__slots__の説明によると予め記憶領域だけ確保しているみたい。
__slots__を使うと、__dict__が作られないから、省メモリ化が必要なアプリとかでは重宝するみたい。
ただ単に属性を勝手に追加させたくないだけなら、__setattr__でも実装できるよなぁ、と思ってたらそういう目的だったのね。
省メモリ化はしたいけど、属性が必要になった時には追加できるようにするには、
__slots__ を”宣言する”時に __dict__を追加すれば良い。
>>> class A(object): ... __slots__ = ['hoge', 'piyo', '__dict__'] ... >>> x = A() >>> dir(x) ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'hoge', 'piyo'] >>> x.hoge = 'hoge' >>> x.spam = 'spam' >>> x.spam 'spam' >>> dir(x) ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'hoge', 'piyo', 'spam'] >>>
わざわざ宣言するを""で囲ったのは、ドキュメントを斜め読みして、後でx.__slots__.append('__dict__')ってやって、
代入文を書いたら”AttributeError: 'A' object has no attribute 'spam'”ってエラーを出されちゃったから。
あと、append後に新しいインスタンスを作っても、__slots__にない変数名に対して代入文を書くとエラーが出る。
クラスを宣言する時にちゃんと__dict__を追加しておかないと駄目みたい。
プロパティ
ある属性に対して、アクセス/値の代入/削除/ドキュメント文字列の呼び出しが行われた時の動作を指定できる。
built-in関数のpropertyを使用して行う。property関数は、次のようにして使う。
property([fget[, fset[, fdel[, doc]]]])
Pythonのドキュメントにあるコードを持ってきて動かしたら、
アクセス/代入/削除でことごとく無限ループになってエラー終了。
無限ループになったので、__dict__を使って書き直すと今度はうまく動いた。
あとx.dataを指定してドキュメント文字列を表示しようとするとKeyErrorが出てしまった。
う〜ん、よくわかんない。とりあえずなんか動いた感じ。
なんで動かないのか判んない、だからこれで問題ないって保証も出来ない。
[kobakoba0723@fedora13-intel64 ~]$ cat property.py #!/usr/bin/env python class A(object): def __init__(self): self.data = None def getdata(self): print 'get data', # return self.data return self.__dict__['data'] def setdata(self, value): print 'set %s to data' % value # self.data = value self.__dict__['data'] = value def deldata(self): print 'delete data' # del self.data del self.__dict__['data'] data = property(getdata, setdata, deldata, "property for data") if __name__ == '__main__': x = A() x.data = 10 print x.data del x.data # help(x.data) help(x) x.value = 20 print x.value [kobakoba0723@fedora13-intel64 ~]$ python property.py set None to data set 10 to data get data 10 delete data Help on A in module __main__ object: class A(__builtin__.object) | Methods defined here: | | __init__(self) | | deldata(self) | | getdata(self) | | setdata(self, value) | | ---------------------------------------------------------------------- | Data descriptors defined here: | | __dict__ | dictionary for instance variables (if defined) | | __weakref__ | list of weak references to the object (if defined) | | data | property for data (END) 20 [kobakoba0723@fedora13-intel64 ~]$
参考にしたページ:
Python v2.6.2 ドキュメント データモデル
やっとむでぽん (python)__slots__ *1
Python v2.6.2 ドキュメント 組込み関数
*1:引用の中で[]を使うとちゃんと表示できないんだなぁ