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.v
bả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 = 3
ghi đè 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_GenericSetAttrWithDict
dườ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 = 3
ghi đè một cách mù quáng v
thay 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_setattro
là 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: VocalDescriptor
khô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 = 3
có 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 = 3
cũng sẽ kích hoạt __set__
.