一般にデスクリプタとは、 特殊な ``束縛に関する動作 (binding behaviour)'' をもつオブジェクト属性のことです。デスクリプタは、デスクリプタ プロトコル (descriptor protocol) のメソッド: __get__(), __set__(), および __delete__() を使って、属性アクセスをオーバライドしているものです。 これらのメソッドのいずれかがオブジェクトに対して定義されている場合、 オブジェクトはデスクリプタであるといいます。
属性アクセスのデフォルトの動作は、オブジェクトの辞書から値を取り出したり、
値を設定したり、削除したりするというものです。例えば、a.x
による
属性の検索では、まず a.__dict__['x']
、次に
type(a).__dict__['x']
、そしてtype(a)
の基底クラスで
メタクラスでないものに続く、といった具合に連鎖が起こります。
しかしながら、検索対象となる値が、デスクリプタメソッドのいずれかを 定義しているオブジェクトの属性値である場合、Python はデフォルトの動作を オーバライドして、デスクリプタメソッドの方を呼び出します。
前後する呼び出し連鎖の中のどこでデスクリプタメソッドが呼び出されるかは、 どのデスクリプタメソッドが定義されているかと、どうやってデスクリプタ メソッドが呼ばれるかに依存します。デスクリプタは新しい形式のオブジェクトや クラス (object() や type() をサブクラス化したもの) だけに 対して呼び出されるので注意してください。
デスクリプタ呼び出しの基点となるのは、属性名への束縛 (binding) 、すなわち
a.x
です。引数がどのようにデスクリプタに結合されるかは a
に
依存します:
x.__get__(a)
を行うというものです。
a.x
は呼び出し:
type(a).__dict__['x'].__get__(a, type(a))
に変換されます。
A.x
は呼び出し: A.__dict__['x'].__get__(None, A)
に変換されます。
a
が super
のインスタンスである場合、束縛 super(B, obj).m()
を行うと
まず A
、続いて B
に対して obj.__class_.__mro__
を
検索し、次に呼び出し: A.__dict__['m'].__get__(obj, A)
で
デスクリプタを呼び出します。
インスタンス束縛では、デスクリプタ呼び出しの優先順位はどのデスクリプタが 定義されているかに依存します。データデスクリプタでは、 __get__() と __set__() を定義します。非データ デスクリプタには __get__() メソッドしかありません。 インスタンス辞書内で属性値が再定義されても、データデスクリプタは常に この値をオーバライドします。対照的に、非データデスクリプタの 場合には、属性値はインスタンス側でオーバライドされます。
(staticmethod() や classmethod() を含む) Python メソッドは、非データデスクリプタとして実装されています。その結果、 インスタンスではメソッドを再定義したりオーバライドできます。 このことにより、個々のインスタンスが同じクラスの他のインスタンスと 互いに異なる動作を獲得することができます。
property() 関数はデータデスクリプタとして実装されています。 従って、インスタンスはあるプロパティの動作をオーバライドすることが できません。
ご意見やご指摘をお寄せになりたい方は、 このドキュメントについて... をご覧ください。