인스턴스 __dict__에 같은 이름을 넣었는데 property가 먼저 읽히는 이유는 무엇인가요?
잠깐, 답변을 보기 전에
먼저 내 말로 설명해 볼까요?
답변과 점검 포인트 펼치기답변 접기
구두 답변
정의를 외우기보다, 원리와 조건을 연결해 설명해 보세요.
property가 클래스에 있으면 data descriptor이기 때문입니다. obj.value를 평가할 때 Python은 먼저 type(obj)의 MRO에서 value를 찾고, 그 클래스 속성이 __set__ 또는 __delete__를 제공하는지 확인합니다. 제공한다면 인스턴스 __dict__의 같은 키보다 descriptor의 __get__이 먼저 호출됩니다. 따라서 다음 상태에서는 사전에 99가 들어 있어도 점 표기법은 10을 반환합니다.
class C:
@property
def value(self): return 10
c = C()
c.__dict__["value"] = 99
print(c.value) # 10
print(c.__dict__["value"]) # 99
T0에는 클래스 후보가 property이고 인스턴스 사전에 value: 99가 있습니다. T1에 property가 data descriptor로 판정되고, T2에 property.__get__(c, C)가 실행되어 10이 나옵니다. 사전 직접 읽기는 이 규칙을 우회하는 별도 경로입니다. c.value = 20도 사전에 20을 쓰는 것이 아니라 property setter로 갑니다. setter가 없으면 AttributeError가 나므로 “읽기는 가리고 쓰기는 저장”이라고 추측하면 안 됩니다. 반대로 함수처럼 __get__만 제공하는 non-data descriptor는 인스턴스 값이 우선입니다. c.f = lambda: ...로 메서드를 shadowing할 수 있는 이유가 이것입니다. 이때 del c.f를 수행하면 인스턴스 사전의 후보가 사라져 다음 조회에서 다시 함수의 __get__이 실행됩니다. 같은 이름을 직접 사전에 넣는 것과 property setter를 호출하는 것은 저장 경로부터 다르므로 디버깅 출력도 두 경로로 나누어야 합니다. 특히 setter가 내부 _value를 갱신하는 구현이라면 vars(c)에서 value가 아니라 _value가 바뀌는지 확인해야 합니다.
득점 포인트
이해가 드러나는 지점을 확인해 보세요.
__set__·__delete__존재 여부가 data descriptor 판정 기준이며,property는 setter가 없어도 이 분류에 들어간다는 점을 설명합니다.obj.value와obj.__dict__['value']가 서로 다른 접근 계약을 사용한다는 상태를 10과 99로 추적합니다.- 함수 같은 non-data descriptor에서는 인스턴스 사전이 우선이라는 반례를 제시합니다.
감점 포인트
놓치기 쉬운 조건과 오해를 짚어 보세요.
- “인스턴스 사전 값은 항상 먼저 읽힌다”고 말하면 descriptor 우선순위를 거꾸로 설명한 것입니다.
- “모든 descriptor가 property처럼 data descriptor다”라고 일반화하면 함수 shadowing을 설명할 수 없습니다.
- setter 없는 property 대입이 조용히 사전 값을 바꾼다고 하면 실제 결과인
AttributeError를 놓칩니다.
더 파고들 거리
하나 더 질문하며 이해의 범위를 넓혀 보세요.
- descriptor를 인스턴스에 저장했을 때 왜 자동으로
__get__이 실행되지 않는지 타입 사전 검색과 비교해 보세요. __getattribute__를 override한 클래스에서object.__getattribute__로 기본 우선순위를 보존하는 방법을 검증해 보세요.
득점·감점 포인트는 학습용 자기 점검 기준이며, 특정 회사의 실제 채점표가 아닙니다.