เล่นง่าย:
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
คำถามนี้มีการทำสำเนาที่มีประสิทธิภาพแต่การทำซ้ำไม่ได้รับคำตอบและฉันขุดลงในแหล่งที่มาของ CPython เป็นแบบฝึกหัดการเรียนรู้อีกเล็กน้อย คำเตือน: ฉันเข้าไปในวัชพืช ฉันจริงๆหวังว่าฉันจะได้รับความช่วยเหลือจากกัปตันที่รู้น้ำเหล่านั้น ฉันพยายามอย่างชัดเจนที่สุดเท่าที่จะเป็นไปได้ในการติดตามการโทรที่ฉันกำลังดูเพื่อผลประโยชน์ในอนาคตของฉันเองและประโยชน์ของผู้อ่านในอนาคต
ฉันเห็นหมึกจำนวนมากหกใส่ลงบนลักษณะการ__getattribute__
ใช้งานของคำอธิบายเช่นการค้นหามาก่อน งูหลาม snippet ใน"อัญเชิญอธิบาย"เพียงด้านล่างFor classes, the machinery is in type.__getattribute__()...
ประมาณตกลงในใจของฉันกับสิ่งที่ผมเชื่อว่าเป็นที่สอดคล้องแหล่ง CPythonในtype_getattro
ซึ่งผมติดตามลงโดยดูที่"tp_slots"แล้วที่ tp_getattro เป็นประชากร และความจริงที่ว่าการB.v
พิมพ์ครั้งแรกนั้น__get__, obj=None, objtype=<class '__main__.B'>
สมเหตุสมผลสำหรับฉัน
สิ่งที่ฉันไม่เข้าใจคือทำไมมอบหมายB.v = 3
สุ่มสี่สุ่มห้าเขียนทับบ่งมากกว่าวิกฤติv.__set__
? ผมพยายามที่จะติดตามการโทร CPython เริ่มต้นอีกครั้งจาก"tp_slots"แล้วมองหาที่ที่มีประชากร tp_setattroแล้วมองไปที่type_setattro type_setattro
ดูเหมือนจะเป็นเสื้อคลุมบาง ๆ รอบ_PyObject_GenericSetAttrWithDict และมีปมของความสับสนของฉัน: _PyObject_GenericSetAttrWithDict
ดูเหมือนจะมีตรรกะที่ให้ความสำคัญกับคำอธิบายถึงของ__set__
วิธี !! กับในใจฉันไม่สามารถคิดออกว่าทำไมB.v = 3
สุ่มสี่สุ่มห้าเขียนทับมากกว่าวิกฤติv
v.__set__
ข้อสงวนสิทธิ์ 1: ฉันไม่ได้สร้าง Python ใหม่จากแหล่งที่มาด้วย printfs ดังนั้นฉันไม่แน่ใจว่าtype_setattro
สิ่งที่ถูกเรียกระหว่างB.v = 3
นั้น
ข้อจำกัดความรับผิดชอบ 2: VocalDescriptor
ไม่ได้มีไว้เพื่อเป็นตัวอย่างคำจำกัดความตัวอธิบาย "ปกติ" หรือ "แนะนำ" มันไม่มีทางเลือกที่จะบอกฉันเมื่อมีการเรียกวิธีการ
__get__
ทำงานได้มากกว่าที่จะ__set__
ทำไม่ได้
__get__
เมธอด ได้เขียนทับได้อย่างมีประสิทธิภาพแอตทริบิวต์ที่มีB.v = 3
int
__get__
มีการเรียกและการใช้งานเริ่มต้นของobject.__getattribute__
และtype.__getattribute__
เรียกใช้__get__
เมื่อใช้อินสแตนซ์หรือคลาส การกำหนดผ่าน__set__
เป็นเพียงตัวอย่างเท่านั้น
__get__
เมธอดdescriptors ควรถูกเรียกใช้เมื่อเรียกใช้จากคลาส นี่คือวิธีที่ @classmethods และ @staticmethods จะดำเนินการตามวิธีการแนะนำ @ Jab ฉันสงสัยว่าทำไมB.v = 3
สามารถเขียนทับตัวบอกคลาสได้ จากการใช้งาน CPython ฉันคาดว่าB.v = 3
จะเปิดใช้งาน__set__
เช่นกัน