BATTER WAY 29. 게터와 세터 메서드 대신에, 일반 속성을 사용하자.
- 다른 언어를 사용하다가, 파이썬으로 넘어온 프로그래머들은 자연스럽게 클래스에 게터(getter)와 세터(setter) 메서드를 명시적으로 구현하려고 한다.
class OldResistor(object):
  def __init__(self, ohms):
    self._ohms = ohms
  def get_ohms(self):
    return self._ohms
  def set_ohms(self, ohms):
    self._ohms = ohms
r0 = OldResistor(50e3)
print(f'Before: {r0.get_ohms()}') # Before: 50000.0
r0.set_ohms(10e3)
print(f'Before: {r0.get_ohms()}')')') # After: 10000.0
r0.set_ohms(r0.get_ohms() + 5e3)
- 사용하는 법은 간단하지만, 파이썬 스럽지 않은 코딩 방법이다.
- 겟터와 셋터는 값을 증가시키고 그 값을 바로 반영하기 불편하다.
- 파이썬은 명시적인 게터와 세터를 구현할 일이 거의 없다. 대신 공개 속성부터 구현하기 시작해야 한다.
class Register(object):
  def __init__(self, ohms):
    self.ohms = ohms
    self.voltage = 0
    self.current = 0
r1 = Register(50e3)
r1.ohms = 10e3
r1.ohms += 5e3
- 
만약 속성을 설정할 때, 특별한 동작이 일어나야 한다면 @property데코레이터와 이에 대응하는setter속성을 사용하는 방법으로 바꿀 수 있다.
- 
여기서는 Registor의 새 서브 클래스를 정의하여,voltage프로퍼티를 할당하면,current값이 바뀌도록 해본다.
- 
제대로 동작하려면, 세터와 게터의 이름이 의도한 프로퍼티의 이름과 같아야한다. 
class VoltageResistance(Register):
  def __init__(self, ohms):
    super().__init__(ohms)
    self._voltage = 0
  @property
  def voltage(self):
    return self._voltage
  @voltage.setter
  def voltage(self, voltage):
    self._voltage = voltage
    self.current = self._voltage / self.ohms
r2 = VoltageResistance(1e3)
print(f'Before: {r2.current}') # 0
r2.voltage = 10
print(f'After: {r2.current}')')') # 0.01
- 프로퍼티에 setter를 설정하면, 클래스에 전달된 값들의 타입을 체크하고 값을 검증할 수 있다.
class BoundedResistence(Register):
  def __init__(self, ohms):
    super().__init__(ohms)
    @property
    def ohms(self):
      return self._ohms
    @ohms.setter
    def ohms(self, ohms):
      if ohms <= 0:
      raise ValueError(f"{ohms} ohms must be > 0")
r3 = BoundedResistence(1e3)
r3.ohms = 0
- 프로퍼티에 setter를 설정하면, 클래스에 전달된 값들의 타입을 체크하고 값을 검증할 수 있다.
- 다음은 모든 저항값이 0옴보다 큼을 보장하는 클래스를 정의한 것이다.
- 만약 ohms속성값이 0이거나, 0보다 작으면 예외가 발생하게 된다.
class FixedResistence(Register):
  def __init__(self, ohms):
    super().__init__(ohms)
    @property
    def ohms(self):
      return self._ohms
    @ohms.setter
    def ohms(self, ohms):
      if hasattr(self, '_ohms'):
        raise ValueError("Can't set attribute")
        self._ohms = ohms
- 부모 클래스의 속성을 불변(Immutable)로 만드는데도 @property를 사용할 수 있다.
핵심 정리
- 
간단한 공개 속성을 사용하여 새 클래스 인터페이스를 정의하고, 세터와 게터 메서드는 사용하지 말자. 
- 
객체의 속성에 접근할 때 특별한 동작을 정의하려면 @property를 사용하자.
- 
@property메서드에서 최소 놀람 규칙을 따르고 이상한 부작용을 피하자.
- 
@property메서드가 빠르게 동작하도록 만들자. 느리거나 복잡한 작업은 일반 메서드로 하자.