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.vzunä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_GenericSetAttrWithDictscheint eine Logik__set__ zu haben , die der Methode eines Deskriptors Vorrang einräumt !! Vor diesem Hintergrund kann ich nicht herausfinden, warum B.v = 3blind überschrieben vund 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_setattrowie es während des Aufrufs aufgerufen wird B.v = 3.
Haftungsausschluss 2: VocalDescriptorsoll 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 = 3hat 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 = 3der Klassendeskriptor überschrieben werden kann. Basierend auf der CPython-Implementierung hatte ich erwartet B.v = 3, auch auszulösen __set__.