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.vpoczą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_GenericSetAttrWithDictwydaje 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, va 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_setattrojak to się nazywa B.v = 3.
Zastrzeżenie 2: VocalDescriptornie 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 = 3skutecznie 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 = 3można zastąpić deskryptor klasy. W oparciu o implementację CPython spodziewałem B.v = 3się również, że zadziała __set__.