728x90

파이썬은 고도의 동적 언어로, 객체의 속성(Attribute)에 접근할 때 내부적으로 복잡하면서도 정교한 메커니즘을 거칩니다. 개발자는 __getattr__와 __getattribute__라는 매직 메서드를 통해 이 접근 과정을 가로채거나 수정할 수 있습니다. 하지만 이 두 메서드는 호출되는 시점과 방식이 판이하게 다르며, 잘못 구현할 경우 시스템을 중단시키는 '무한 루프(Infinite Recursion)'에 빠지기 쉽습니다. 본 가이드에서는 파이썬 시니어 개발자가 반드시 숙지해야 할 속성 접근의 우선순위와 안전한 구현 방법을 심층 분석합니다.
1. 매직 메서드의 정의와 결정적 차이
속성 접근을 제어하는 두 메서드는 이름은 비슷하지만, 파이썬 인터프리터에 의해 처리되는 논리가 완전히 다릅니다.
- __getattribute__(self, name): 객체의 속성에 접근할 때 무조건적으로 가장 먼저 호출됩니다. 속성이
__dict__에 존재하든, 클래스 변수이든 상관없이 모든 접근을 가로챕니다. - __getattr__(self, name): 일반적인 속성 검색 결과가 AttributeError를 발생시킬 때만, 즉 '속성을 찾지 못했을 때' 마지막 수단으로 호출됩니다.
2. 호출 우선순위 및 동작 메커니즘 비교
파이썬이 속성 obj.x를 찾는 과정을 단계별로 비교하면 다음과 같습니다.
| 비교 항목 | __getattribute__ | __getattr__ |
|---|---|---|
| 호출 시점 | 모든 속성 접근 시 (필수 호출) | 속성을 찾을 수 없을 때만 (선택 호출) |
| 우선순위 | 최상위 (1순위) | 최하위 (모든 시도 실패 후) |
| 무한 루프 위험 | 매우 높음 (내부 접근 시 발생) | 낮음 (존재하지 않는 속성 재귀 호출 시 발생) |
| 주요 목적 | 강력한 접근 제어, 보안, 프록시 구현 | 기본값 제공, 동적 속성 생성, 지연 로딩 |
3. 무한 루프의 원인과 치명적인 실수
가장 빈번하게 발생하는 실수는 __getattribute__ 내부에서 다시 self.name이나 self.__dict__를 사용하여 속성에 접근하는 것입니다. 이 경우 인터프리터는 다시 __getattribute__를 호출하게 되어 스택 오버플로(Stack Overflow)가 발생합니다.
해결 방법: super()의 활용
무한 루프를 방지하기 위해서는 반드시 상위 클래스의 메서드인 super().__getattribute__(name)를 호출하여 파이썬의 표준 속성 검색 엔진을 이용해야 합니다.
4. [Sample Example] 안전한 속성 제어 구현
다음은 동적 로깅 기능을 갖춘 안전한 속성 접근 클래스 예제입니다.
class SmartProxy:
def __init__(self, target):
# __setattr__ 오버라이딩을 피하기 위해 직접 __dict__ 조작
object.__setattr__(self, "_target", target)
def __getattribute__(self, name):
print(f"로그: '{name}' 속성에 접근을 시도합니다.")
# 무한 루프 방지를 위해 super() 사용
try:
return super().__getattribute__(name)
except AttributeError:
# 여기서 getattr 호출로 넘김
return self.__getattr__(name)
def __getattr__(self, name):
print(f"경고: '{name}' 속성이 존재하지 않습니다. 동적 처리를 실행합니다.")
return f"Dynamic value for {name}"
# 실행 예시
proxy = SmartProxy({"data": 100})
print(proxy._target) # __getattribute__ 통과 후 출력
print(proxy.unknown) # __getattribute__ -> __getattr__ 순으로 호출
5. 실무 적용 시 고려해야 할 3가지 팁
- 성능 고려:
__getattribute__는 모든 접근에 오버헤드를 발생시킵니다. 단순히 속성이 없을 때의 처리가 목적이라면__getattr__가 훨씬 효율적입니다. - 상태 관리: 인스턴스 초기화(
__init__) 중에 속성을 설정할 때도__getattribute__가 호출될 수 있음을 유의하십시오. - 내장 함수와의 관계:
len()이나str()같은 내장 함수는 효율성을 위해 인스턴스 딕셔너리보다 클래스 슬롯을 먼저 참조하므로, 이 매직 메서드들이 기대한 대로 작동하지 않을 수 있습니다.
6. 내용 출처
- Python Official Documentation - Customizing attribute access (Section 3.3.2)
- "Fluent Python" by Luciano Ramalho - Chapter 19: Dynamic Attributes and Properties
- CPython Source Code - Objects/typeobject.c
728x90