Repro sederhana:
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
Pertanyaan ini memiliki duplikat yang efektif , tetapi duplikat itu tidak dijawab, dan saya menggali lebih dalam sumber CPython sebagai latihan pembelajaran. Peringatan: saya pergi ke gulma. Saya benar-benar berharap bisa mendapatkan bantuan dari seorang kapten yang mengetahui perairan itu . Saya mencoba untuk menjadi sejelas mungkin dalam melacak panggilan yang saya lihat, untuk keuntungan saya di masa depan dan manfaat pembaca di masa depan.
Saya telah melihat banyak tinta tumpah karena perilaku yang __getattribute__diterapkan pada deskriptor, misalnya pencarian prioritas. Python potongan di "menyerukan Deskriptor" tepat di bawah For classes, the machinery is in type.__getattribute__()...kira-kira setuju dalam pikiran saya dengan apa yang saya percaya adalah yang sesuai sumber CPython di type_getattro, yang saya melacak dengan melihat "tp_slots" maka di mana tp_getattro dihuni . Dan fakta bahwa B.vawalnya mencetak __get__, obj=None, objtype=<class '__main__.B'>masuk akal bagi saya.
Yang tidak saya mengerti adalah, mengapa tugas itu B.v = 3secara buta menimpa deskriptor, bukannya memicu v.__set__? Saya mencoba melacak panggilan CPython, mulai sekali lagi dari "tp_slots" , kemudian melihat di mana tp_setattro diisi , kemudian melihat type_setattro . type_setattro tampaknya menjadi pembungkus tipis di sekitar _PyObject_GenericSetAttrWithDict . Dan ada inti dari kebingungan saya: _PyObject_GenericSetAttrWithDicttampaknya memiliki logika yang memberikan diutamakan untuk keterangan ini __set__metode !! Dengan mengingat hal ini, saya tidak tahu mengapa B.v = 3menindih secara membabi buta vdaripada memicu v.__set__.
Penafian 1: Saya tidak membangun kembali Python dari sumber dengan printfs, jadi saya tidak sepenuhnya yakin type_setattroapa yang dipanggil selama B.v = 3.
Penafian 2: VocalDescriptortidak dimaksudkan untuk memberikan contoh definisi deskriptor "tipikal" atau "direkomendasikan". Ini adalah no-op verbose untuk memberi tahu saya ketika metode dipanggil.
__get__bekerja sama sekali, daripada mengapa __set__tidak.
__get__metode ini. B.v = 3telah secara efektif menimpa atribut dengan int.
__get__dipanggil, dan implementasi default object.__getattribute__dan type.__getattribute__memanggil __get__ketika menggunakan instance atau kelas. Penugasan via __set__hanya untuk instance.
__get__metode deskriptor seharusnya dipicu ketika dipanggil dari kelas itu sendiri. Ini adalah bagaimana @classmethods dan @staticmethods diimplementasikan, sesuai dengan panduan cara . @ Jab Saya bertanya-tanya mengapa B.v = 3bisa menimpa deskriptor kelas. Berdasarkan implementasi CPython, saya berharap B.v = 3juga memicu __set__.