Einfache Reproduktion:
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
Diese Frage hat ein effektives Duplikat , aber das Duplikat wurde nicht beantwortet, und ich habe mich als Lernübung etwas mehr mit der CPython-Quelle befasst. Achtung: Ich bin ins Unkraut gegangen. Ich hoffe wirklich, dass ich Hilfe von einem Kapitän bekommen kann, der diese Gewässer kennt . Ich habe versucht, die Anrufe, die ich mir ansah, so explizit wie möglich zu verfolgen, zu meinem eigenen zukünftigen Nutzen und zum Nutzen zukünftiger Leser.
Ich habe viel Tinte über das Verhalten von __getattribute__
Deskriptoren verschüttet gesehen , z. B. Vorrang bei der Suche. Die Python - Code - Schnipsel in „Hervorrufen Descriptors“ knapp unter For classes, the machinery is in type.__getattribute__()...
etwa in meinem Kopf stimmt , was ich glaube , dass die entsprechende CPython Quelle in type_getattro
, die ich durch einen Blick auf aufgespürt „tp_slots“ dann wo tp_getattro bevölkert ist . Und die Tatsache, dass B.v
zunächst gedruckt wird, __get__, obj=None, objtype=<class '__main__.B'>
macht für mich Sinn.
Was ich nicht verstehe ist, warum B.v = 3
überschreibt die Zuweisung den Deskriptor blind, anstatt ihn auszulösen v.__set__
? Ich habe versucht, den CPython-Aufruf zu verfolgen, indem ich noch einmal von "tp_slots" aus angefangen habe , dann nachgesehen habe , wo tp_setattro gefüllt ist , und dann nach type_setattro . type_setattro
scheint ein dünner Wrapper um _PyObject_GenericSetAttrWithDict zu sein . Und da ist der Kern meiner Verwirrung: Es _PyObject_GenericSetAttrWithDict
scheint eine Logik__set__
zu haben , die der Methode eines Deskriptors Vorrang einräumt !! Vor diesem Hintergrund kann ich nicht herausfinden, warum B.v = 3
blind überschrieben v
und nicht ausgelöst wird v.__set__
.
Haftungsausschluss 1: Ich habe Python nicht mit printfs aus dem Quellcode neu erstellt, daher bin ich mir nicht ganz sicher, type_setattro
wie es während des Aufrufs aufgerufen wird B.v = 3
.
Haftungsausschluss 2: VocalDescriptor
soll nicht als Beispiel für eine "typische" oder "empfohlene" Deskriptordefinition dienen. Es ist ein ausführliches No-Op, mir zu sagen, wann die Methoden aufgerufen werden.
__get__
überhaupt gearbeitet hat und nicht warum __set__
.
__get__
Methode weiterhin aufgerufen wird . B.v = 3
hat das Attribut effektiv mit einem überschrieben int
.
__get__
genannt wird , und die Standard - Implementierungen object.__getattribute__
und type.__getattribute__
invoke , __get__
wenn eine Instanz oder die Klasse. Das Zuweisen über __set__
ist nur eine Instanz.
__get__
Methoden der Deskriptoren sollen ausgelöst werden, wenn sie von der Klasse selbst aufgerufen werden. Auf diese Weise werden @classmethods und @staticmethods gemäß der Anleitung implementiert . @Jab Ich frage mich, warum B.v = 3
der Klassendeskriptor überschrieben werden kann. Basierend auf der CPython-Implementierung hatte ich erwartet B.v = 3
, auch auszulösen __set__
.