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.v
awalnya mencetak __get__, obj=None, objtype=<class '__main__.B'>
masuk akal bagi saya.
Yang tidak saya mengerti adalah, mengapa tugas itu B.v = 3
secara 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_GenericSetAttrWithDict
tampaknya memiliki logika yang memberikan diutamakan untuk keterangan ini __set__
metode !! Dengan mengingat hal ini, saya tidak tahu mengapa B.v = 3
menindih secara membabi buta v
daripada memicu v.__set__
.
Penafian 1: Saya tidak membangun kembali Python dari sumber dengan printfs, jadi saya tidak sepenuhnya yakin type_setattro
apa yang dipanggil selama B.v = 3
.
Penafian 2: VocalDescriptor
tidak 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 = 3
telah 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 = 3
bisa menimpa deskriptor kelas. Berdasarkan implementasi CPython, saya berharap B.v = 3
juga memicu __set__
.