タイトルの通り、__get__
とか __set__
メソッドを実装したクラスの機能について。
python のバージョンは 2.7。
ディスクリプタと呼ぶらしい。こんな公式ドキュメントがある。
http://docs.python.jp/2/howto/descriptor.html
また、理解する上でこちらが大変参考になった。
Python を支える技術 ディスクリプタ編 #pyconjp - Qiita
(スライド版:https://speakerdeck.com/knzm/python-wozhi-eruji-shu-deisukuriputabian)
ディスクリプタとは
以下のように __get__, __set__, __delete__
メソッドを定義したクラスのこと。
class Descriptor(object): def __get__(self, obj, obj_type=None): pass def __set__(self, obj, value): pass def __delete__(self, obj): pass
どうやって使うのか?
まず重要なこととして、ディスクリプタはそのクラス単体ではあまり意味がない。
他のクラスの属性にこのディスクリプタを設定することで、クラスの特定の属性へのアクセスをカスタマイズする ために用いる。
このディスクリプタを利用する側の、親となるクラスのことを
公式ドキュメントではオーナ (owner) クラスなどと呼んでいる。
サンプルコード
公式ドキュメント を参考にした。
# ディスクリプタ class MyDescriptor(object): def __init__(self, value=None, name='attr'): self.value = value self.name = name def __get__(self, obj, obj_type=None): print 'Get "%s": %r' % (self.name, self.value) return self.value def __set__(self, obj, value): print 'Set "%s": %r' % (self.name, value) self.value = value
# インタラクティブシェルで実行 >>> class MyClass(object): # owner クラス ... x = MyDescriptor(10, 'attribute x') ... y = 5 ... >>> m = MyClass() >>> m.x Get "attribute x": 10 10 >>> m.y 5 >>> m.x = 20 Set "attribute x": 20 >>> m.y = 50 >>> m.x Get "attribute x": 10 10 >>> m.y 50
クラス MyClass
の x
という属性へアクセスするときだけ挙動をカスタマイズできていることがわかる。
プロパティ(@property) と何が違うの?
特定の属性へのアクセスをカスタマイズするための手段としてはディスクリプタのほか、
プロパティ (property()
) という関数がある。
http://docs.python.jp/2/library/functions.html#property
@property
のようにデコレータとして使う方が一般的だろうか。
先ほどの例は、プロパティを使って次のように書くこともできる。
class MyClass(object): y = 5 def __init__(self): self._x = 10 self.name = 'attr x' @property def x(self): print 'Get "%s": %r' % (self.name, self._x) return self._x @x.setter def x(self, value): print 'Set "%s": %r' % (self.name, value) self._x = value
両者の違いだが、プロパティはディスクリプタの一種。ディスクリプタの方が汎用性が高い。
また、プロパティは特定のクラスに直接定義するのに対し、ディスクリプタはクラスとは独立して定義することができる。
__getattribute__ と何が違うの?
__getattribute__ でも属性アクセスをカスタマイズできるが、クラスの全部の属性アクセスが変更される。
一方、ディスクリプタを使うと 特定の属性のみ アクセスをカスタマイズできる。
その他
X.Y
という書き方でクラス/インスタンスの属性にアクセスした時、
X
がクラスかインスタンスかY
がプロパティかメソッドか通常の属性か
によって内部的に起こっていることが違うらしい。
そこらへん、上でも挙げたこの記事に非常に詳しく書かれているので勉強したい。
Python を支える技術 ディスクリプタ編 #pyconjp - Qiita
リファレンス
上で紹介したものも含む。