Repro đơn giản:
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
Câu hỏi này có một bản sao hiệu quả , nhưng bản sao không được trả lời và tôi đã đào sâu thêm một chút vào nguồn CPython như một bài tập học tập. Cảnh báo: tôi đã đi vào đám cỏ dại. Tôi thực sự hy vọng tôi có thể nhận được sự giúp đỡ từ một thuyền trưởng biết những vùng biển đó . Tôi đã cố gắng rõ ràng nhất có thể trong việc truy tìm các cuộc gọi mà tôi đang tìm kiếm, vì lợi ích tương lai của chính tôi và lợi ích của các độc giả tương lai.
Tôi đã thấy rất nhiều mực tràn ra hành vi __getattribute__áp dụng cho các mô tả, ví dụ như ưu tiên tra cứu. Các Python đoạn trích trong "Gọi Phần mô tả" ngay dưới For classes, the machinery is in type.__getattribute__()...khoảng đồng ý bằng tâm trí của tôi với những gì tôi tin là tương ứng với nguồn CPython trong type_getattro, mà tôi theo dõi xuống bằng cách nhìn vào "tp_slots" thì nơi tp_getattro được phổ biến . Và thực tế là B.vbản in ban đầu __get__, obj=None, objtype=<class '__main__.B'>có ý nghĩa với tôi.
Điều tôi không hiểu là tại sao bài tập lại B.v = 3ghi đè một cách mù quáng vào phần mô tả, thay vì kích hoạt v.__set__? Tôi đã cố gắng theo dõi cuộc gọi CPython, bắt đầu lại từ "tp_slots" , sau đó nhìn vào nơi tp_setattro được điền , sau đó nhìn vào type_setattro . type_setattro dường như là một trình bao bọc mỏng xung quanh _PyObject_GenericSetAttrWithDict . Và có mấu chốt của sự nhầm lẫn của tôi: _PyObject_GenericSetAttrWithDictdường như có logic ưu tiên cho __set__phương pháp của một người mô tả !! Với suy nghĩ này, tôi không thể hiểu tại sao lại B.v = 3ghi đè một cách mù quáng vthay vì kích hoạt v.__set__.
Tuyên bố miễn trừ trách nhiệm 1: Tôi đã không xây dựng lại Python từ nguồn bằng các bản in, vì vậy tôi không hoàn toàn chắc chắn type_setattrolà những gì được gọi trong thời gian này B.v = 3.
Tuyên bố miễn trừ trách nhiệm 2: VocalDescriptorkhông nhằm mục đích minh họa cho định nghĩa mô tả "điển hình" hoặc "khuyến nghị". Đó là một no-op dài dòng để cho tôi biết khi nào các phương thức đang được gọi.
__get__làm việc cả, hơn là tại sao __set__không.
__get__phương thức. B.v = 3đã ghi đè lên thuộc tính một cách hiệu quả với một int.
__get__được gọi hay không, và các cài đặt mặc định object.__getattribute__và type.__getattribute__gọi __get__khi sử dụng một thể hiện hoặc lớp. Chỉ định thông qua __set__là chỉ thể hiện.
__get__các phương thức của mô tả được cho là kích hoạt khi được gọi từ chính lớp đó. Đây là cách @ classmethods và @staticmethods được thực hiện, theo hướng dẫn cách làm . @Jab Tôi tự hỏi tại sao B.v = 3có thể ghi đè lên mô tả lớp. Dựa trên việc thực hiện CPython, tôi dự kiến B.v = 3cũng sẽ kích hoạt __set__.