데코레이터 @property는 메서드를 클래스의 "가상" 속성으로 변환할 수 있게 해줍니다: 클래스 사용자에게는 일반 속성처럼 보이지만 Python 함수의 논리에 의해 제어됩니다. 원래 파이썬에서 모든 인스턴스 속성은 직접적으로 접근할 수 있었지만, 캡슐화를 지원하기 위해 인터페이스를 변경하지 않고 데이터 접근을 제어할 수 있는 메커니즘이 필요했습니다.
역사:
초기 파이썬 버전에서는 속성에 대한 접근을 제한할 수 있는 명시적인 메커니즘이 없었습니다. 캡슐화 문제는 규약(예: 언더스코어)으로 해결되었지만, 값의 저장/유효성 검증 논리를 변경하는 것은 호환성을 깨뜨리게 되었습니다. @property 데코레이터가 도입됨에 따라 메서드를 속성으로 선언하고, 게터와 세터, 딜리터를 추가하여 클래스의 이전 인터페이스를 유지할 수 있게 되었습니다.
문제:
나중에 필드의 유효성 검사나 값 계산 논리를 추가해야 할 경우, 전체 인터페이스를 재구성하는 것은 비용이 많이 듭니다 — 변수에 대한 모든 접근 지점을 변경해야 합니다. 이 때, 필드의 내부 구현이 클래스의 외부 소비자에게 노출되지 않아야 합니다. @property를 사용하면 접근을 쉽게 "가상화"할 수 있습니다.
해결책:
@property는 "인터페이스 보호가 있는 게터/세터 패턴"을 구현합니다: 계산 가능한 속성을 추가하거나, 클라이언트 코드를 변경하지 않고 값 설정을 제어할 수 있습니다.
코드 예:
class Temperature: def __init__(self, celsius): self._celsius = celsius @property def celsius(self): return self._celsius @celsius.setter def celsius(self, value): if value < -273.15: raise ValueError("절대 영도보다 낮은 온도입니다!") self._celsius = value @property def fahrenheit(self): return self._celsius * 9 / 5 + 32 # 사용 예 obj = Temperature(25) print(obj.celsius) # 25 print(obj.fahrenheit) # 77.0 obj.celsius = -300 # ValueError
주요 특징:
모든 클래스 속성에 property를 추가할 수 있으며 하위 호환성을 깨지 않습니까?
아니요. 속성이 공용이고 일반 변수로 사용되었다면 property로의 대체가 투명하게 가능합니다. 그러나 어느 곳에서 __dict__를 통해 접근하거나 type(obj.attr)를 통해 타입 검사를 수행했다면 예상치 못한 동작이 발생할 수 있습니다.
속성을 위한 세터만 선언할 수 있습니까?
아니요, 우선 게터가 필수입니다(@property가 있는 메서드), 그렇지 않으면 파이썬은 세터와 "연결"할 속성이 무엇인지 알 수 없습니다.
데코레이터 @property/@setter/@deleter의 순서는 무엇이며 그 이유는 무엇입니까?
항상 먼저 @property를 작성해야 합니다(속성 객체를 생성합니다), 그런 다음 메서드 이름을 통해 @<name>.setter와 @<name>.deleter를 추가합니다(이들은 이전에 생성된 property를 보완합니다).
class A: @property def value(self): ... @value.setter def value(self, v): ...
클래스 속성이 공개되었던 경우, 이후에 공개 API 출시 후 property를 통해 계산된 속성을 추가했습니다. 이전 코드에서 속성에 대한 접근이 암묵적으로 동작 방식을 변경하거나 오류를 발생시킵니다.
장점:
단점:
처음 설계 시 모든 필드를 비공개(하나의 언더스코어)로 정의하고 사용자는 메서드/properties를 통해 작업했습니다. 이후 property에 새로운 논리를 추가하는 것이 클라이언트 인터페이스 변경 없이 이루어졌습니다.
장점:
단점: