デスクリプタとは、get、set、__delete__のいずれかのメソッドを実装するオブジェクトです。これにより、クラスの属性へのアクセスを制御できます。
デスクリプタの標準メソッド:
__get__(self, instance, owner)__set__(self, instance, value)__delete__(self, instance)デスクリプタには以下の2種類があります:
使用例:
class OnlyPositive: def __init__(self): self._name = '_value' def __get__(self, instance, owner): return instance.__dict__[self._name] def __set__(self, instance, value): if value < 0: raise ValueError('Value must be >= 0') instance.__dict__[self._name] = value class Account: value = OnlyPositive() def __init__(self, value): self.value = value acc = Account(10) acc.value = -1 # ValueError!
propertyはクラスレベルでデスクリプタを作成するための単なる文法的糖衣です。
デスクリプタをクラスではなくインスタンスの属性として作成した場合、何が起こりますか?
多くの人はデスクリプタが機能すると考えていますが、そうではありません。
正しい答え:
デスクリプタは、クラスの属性として直接定義された場合にのみ機能します。インスタンスの属性としてデスクリプタを設定すると、get/__set__メソッドは呼び出されず、通常の属性へのアクセスの論理が適用されます。
歴史
完全なデスクリプタの代わりにpropertyを使用
バリデーションやその他の関連属性に対して、開発者は単純なpropertyを使用し、これがロジックの重複と異なるクラス間でのコードの再利用不可能につながっていました。
歴史
ストレージ再利用時の不変性の遵守の不履行
デスクリプタは、インスタンスの内部ストレージではなく、自分のクラスの内部属性(self.x)にデータを保存していたため、属性が「共有」され、異なるインスタンスによって値が上書きされる — オブジェクト間でデータが「漏れ」ました。
歴史
データデスクリプタと非データデスクリプタを混同
複雑な階層で__set__の実装をスキップしたため、インスタンスの通常の属性がデスクリプタを上書きし、バリデーションメカニズム全体が壊れてしまいました — バグは常に現れず、デバッグが難しいものでした。