간단한 재현 :
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__
예를 들어 조회 우선 순위와 같은 설명자 에 적용되는 동작으로 인해 많은 잉크가 쏟아지는 것을 보았습니다 . 아래의 "Descriptors 호출"에 있는 Python 스 니펫은 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 : printfs를 사용하여 소스에서 Python을 다시 작성하지 않았기 때문에 type_setattro
에서 호출되는 것이 확실하지 않습니다 B.v = 3
.
고지 사항 2 : VocalDescriptor
"일반"또는 "권장"설명자 정의를 예로 들지 않습니다. 메소드가 호출되는 시점을 알려주는 것은 장황한 일이 아닙니다.
__get__
것이 아니라 전혀 효과 __set__
가 없었습니다.
__get__
메소드를 호출 할 것으로 예상합니다 . B.v = 3
로 속성을 효과적으로 덮어 썼습니다 int
.
__get__
와 기본 구현 object.__getattribute__
및 type.__getattribute__
호출을 결정합니다 __get__
. via를 할당하는__set__
것은 인스턴스 전용입니다.
__get__
클래스 자체에서 호출 될 때 디스크립터의 메소드가 트리거되어야한다고 생각합니다. 사용법 안내서에 따라 @classmethods 및 @staticmethods가 구현되는 방식 입니다. @Jab 왜 B.v = 3
클래스 설명자를 덮어 쓸 수 있는지 궁금 합니다. CPython 구현을 기반으로 나는 B.v = 3
또한 트리거 할 것으로 예상 했다 __set__
.