簡単な再現:
class VocalDescriptor(object):
def __get__(self, obj, objtype):
print('__get__, obj={}, objtype={}'.format(obj, objtype))
def __set__(self, obj, val):
print('__set__')
class B(object):
v = VocalDescriptor()
B.v # prints "__get__, obj=None, objtype=<class '__main__.B'>"
B.v = 3 # does not print "__set__", evidently does not trigger descriptor
B.v # does not print anything, we overwrote the descriptor
この質問には効果的な重複がありますが、重複は回答されなかったため、学習課題としてCPythonソースをもう少し掘り下げました。警告:雑草に入りました。私はそれらの水を知っている船長から助けが得られることを本当に望んでいます。私自身の将来の利益と将来の読者の利益のために、私が見ている電話を追跡する際にできるだけ明確にしようとしました。
__getattribute__ルックアップの優先順位など、記述子に適用された動作に大量のインクがこぼれたことを確認しました。でPythonはスニペット「の起動記述子」は、単に以下のFor classes, the machinery is in type.__getattribute__()...大体私は相当であると考えているものと私の心に同意CPythonのソースでtype_getattro、私が見てによって見つけ出し、「tp_slots」そしてtp_getattroが移入されます。そして、B.v最初に印刷__get__, obj=None, objtype=<class '__main__.B'>するという事実は私には理にかなっています。
私が理解していないのは、割り当てB.v = 3がトリガーするのではなく、なぜ記述子を盲目的に上書きするのv.__set__ですか?「tp_slots」からもう一度CPython呼び出しをトレースし、次にtp_setattroが入力されている場所を調べ、次にtype_setattroを調べました。 type_setattro 以下のように見えるの周りの薄いラッパー_PyObject_GenericSetAttrWithDict。そして、私の混乱の核心があります: _PyObject_GenericSetAttrWithDict持っているように見えます記述子のに優先権を与えるロジック__set__方法を!これを念頭に置いて、トリガーB.v = 3するのvではなく、なぜ盲目的に上書きするのか理解できませんv.__set__。
免責事項1:私はソースからprintfsを使用してPythonを再構築しなかったため、のtype_setattro間に何が呼び出されているのか完全にはわかりませんB.v = 3。
免責事項2: VocalDescriptor「典型的」または「推奨」記述子の定義を例示することを意図したものではありません。メソッドがいつ呼び出されているかを通知するのは、冗長な何もしないことです。
__get__機能し__set__なかったのではなく、なぜ機能したのかです。
__get__メソッドを呼び出すことを期待しています。 B.v = 3効果的に属性をint。
__get__と呼ばれ、デフォルトの実装object.__getattribute__とtype.__getattribute__呼び出し__get__インスタンスまたはクラスを使用する場合。viaの割り当て__set__はインスタンスのみです。