初めてのPython(26章中編)
__getattr__ / __setattr__ / __getattribute__
__getattr__は、オブジェクトツリーに存在しない属性へのアクセスがあった時だけ呼び出される。
__setattr__は、オブジェクトツリーに存在するしない関わらず呼び出される。
[kobakoba0723@fedora13-intel64 ~]$ cat getsetattr.py #!/usr/bin/env python class A(object): def __init__(self): self.hoge = "hoge" self.piyo = "piyo" def __getattr__(self, attrname): print "%s not exist in instance or class tree" % attrname return "spam" def __setattr__(self, attrname, data): if attrname in self.__dict__.keys(): print "%s exists, override" % attrname else: print "%s not exists, create" % attrname self.__dict__[attrname] = data if __name__ == '__main__': x = A() print x.hoge print x.piyo print x.spam x.hoge = "hogehoge" x.spam = "spamer" print x.hoge print x.piyo print x.spam [kobakoba0723@fedora13-intel64 ~]$ python getsetattr.py hoge not exists, create piyo not exists, create hoge piyo spam not exist in instance or class tree spam hoge exists, override spam not exists, create hogehoge piyo spamer [kobakoba0723@fedora13-intel64 ~]$
__getattr__って、オブジェクトツリー上の存在有無に関係なく呼び出されると勘違いしてた。。。
思ってたとおり(?)に呼び出されるのが__getattribute__
[kobakoba0723@fedora13-intel64 ~]$ cat getattribute.py #!/usr/bin/env python class A(object): def __init__(self): self.hoge = "hoge" self.piyo = "piyo" def __getattribute__(self, attrname): # if attrname in self.__dict__.keys(): # return self.attrname # else: # print "%s not exists" % attrname # return "spam" print "Class getattribute invoke" return object.__getattribute__(self, attrname) if __name__ == '__main__': x = A() print x.hoge print x.piyo print x.spam [kobakoba0723@fedora13-intel64 ~]$ python getattribute.py Class getattribute invoke hoge Class getattribute invoke piyo Class getattribute invoke Traceback (most recent call last): File "getattribute.py", line 20, in <module> print x.spam File "getattribute.py", line 14, in __getattribute__ return object.__getattribute__(self, attrname) AttributeError: 'A' object has no attribute 'spam' [kobakoba0723@fedora13-intel64 ~]$
__getattibute__を使って __getattr__相当のことをやろうとして、self.__dict__[attrname]を使おうとしたら、
File "getattribute_err.py", line 8, in __getattribute__ if attrname in self.__dict__.keys():
延々このエラーが表示されて、しまいにはRuntimeError まで。
インスタンスに対して、"."でアクセスすると、__getattribute__が呼ばれて無限ループになってしまうみたい。
__setattr__のなかで、
self.attrname = value
ってやると無限ループになるのと同じ感じかな。
なにか対処しかたがあるんだろうということで公式ドキュメントを見てると
object.__getattribute__(self, name) のように基底クラスのメソッドを同じ属性名を使って呼び出さなければなりません。
と書いてあるので、見よう見まねで書き換えたらどうやらうまくいった。
とはいえ、なんで動くのかよくわからないので、object.__getattribute__を調べてみると、
>>> help(object.__getattribute__) __getattribute__(...) x.__getattribute__('name') <==> x.name (END)指定された属性の値を返してくれるみたいだけど、objectクラスにはhoge/spamなんかの属性は存在しない。 なんで動いているのか調べてみたものの、結果よく判らないまま。