Proste repro:
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
To pytanie ma skuteczny duplikat , ale na duplikat nie udzielono odpowiedzi, a jako ćwiczenie edukacyjne wkopałem trochę więcej w źródło CPython. Ostrzeżenie: poszedłem do chwastów. Naprawdę mam nadzieję, że uda mi się uzyskać pomoc od kapitana, który zna te wody . Starałem się jak najdokładniej śledzić rozmowy, na które patrzyłem, dla własnej korzyści i korzyści dla przyszłych czytelników.
Widziałem dużo atramentu rozlanego na zachowanie __getattribute__
stosowane do deskryptorów, np. Pierwszeństwo wyszukiwania. Python snippet w „Wywoływanie Descriptors” tuż poniżej For classes, the machinery is in type.__getattribute__()...
grubsza zgadza się w moim umyśle, co moim zdaniem jest odpowiednie źródło CPython w type_getattro
, które śledzone przez patrząc na „tp_slots” wtedy gdzie tp_getattro jest zaludnionych . A fakt, że B.v
początkowo drukuje, __get__, obj=None, objtype=<class '__main__.B'>
ma dla mnie sens.
Nie rozumiem tylko, dlaczego przypisanie B.v = 3
ślepo zastępuje deskryptor, a nie wyzwala v.__set__
? Próbowałem prześledzić wywołanie CPython, zaczynając od „tp_slots” , następnie sprawdzając, gdzie jest wypełniony tp_setattro , a potem patrząc na type_setattro . type_setattro
wygląda na cienkie opakowanie wokół _PyObject_GenericSetAttrWithDict . I jest sedno mojego zamieszania: _PyObject_GenericSetAttrWithDict
wydaje się, że ma logikę, która daje pierwszeństwo __set__
metodzie deskryptora !! Mając to na uwadze, nie mogę zrozumieć, dlaczego B.v = 3
ślepo zastępuje, v
a nie wyzwala v.__set__
.
Zastrzeżenie 1: Nie przebudowałem Pythona ze źródła za pomocą printfs, więc nie jestem całkowicie pewien, type_setattro
jak to się nazywa B.v = 3
.
Zastrzeżenie 2: VocalDescriptor
nie ma na celu zilustrowania „typowej” lub „zalecanej” definicji deskryptora. Mówienie mi, kiedy metody są wywoływane, jest pełne.
__get__
w ogóle działało, a nie dlaczego __set__
.
__get__
metodę. B.v = 3
skutecznie zastąpił atrybut wartością int
.
__get__
jest wywoływany, a także domyślne implementacje object.__getattribute__
i type.__getattribute__
wywołanie __get__
przy użyciu instancji lub klasy. Przypisywanie za pośrednictwem __set__
dotyczy tylko instancji.
__get__
metody deskryptorów powinny się uruchamiać po wywołaniu z samej klasy. W ten sposób zaimplementowano metody @classmethods i @staticmethods, zgodnie z instrukcją . @Jab Zastanawiam się, dlaczego B.v = 3
można zastąpić deskryptor klasy. W oparciu o implementację CPython spodziewałem B.v = 3
się również, że zadziała __set__
.