[Pythonic Programming] Property
파이썬의 property는 객체의 속성에 접근할 때 간편하게 getter와 setter 메서드를 정의할 수 있게 해주는 기능입니다. property를 사용하면, 외부에서 직접 접근할 수 있는 속성처럼 보이지만 내부적으로는 특정 로직을 추가하여 속성의 값을 가져오거나 설정할 수 있습니다. property는 decorator의 일종인데요, decorator에 대한 이야기는 추후 다른 포스팅에서 업로드하도록 하겠습니다. 또 property라는 것은 반드시 이렇게 코딩해야만 한다라기 보다는 제가 지난 포스팅에서 언급한바와 같이 "논리적이고 효율적으로" 글을 쓰는 것처럼 코딩도 논리적으로 구조화하려는 노력이라고 보시면 될 것 같습니다.
파이썬은 직접적인 '접근제어'가 존재하지 않습니다. 다만 private method 등은 관습적으로 이름앞에 '_' (under-bar)를 붙여서 표기합니다. 그래서 아래 예시처럼 Person이라는 class의 age라는 속성을 private으로 만들고 싶다면 _age를 정의하고 get_age, set_age와 같은 method를 사용하는 궁여지책을 사용할 수도 있습니다. 정의된 이름의 "꼴"을 다르게해서 프로그래머의 의중을 드러내고는 있지만 기존의 프로그래밍 방식과 본질적으로 다르진 않습니다.
class Person:
def __init__(self):
self.__age = 0
def get_age(self): # getter
return self.__age
def set_age(self, value): # setter
self.__age = value
james = Person()
james.set_age(20)
print(james.get_age())
그러면, property를 사용해서 이러한 작업을 어떻게 pythonic하게 할 수 있을까요? 지난 포스팅에서 살펴보았던 사각형의넓이 예시를 다시 생각해볼까요?
class Rectangle:
def __init__(self, width, height):
# 내부 변수로 실제 값 저장
self._width = width
self._height = height
# width에 대한 getter
@property
def width(self):
return self._width
# width에 대한 setter
@width.setter
def width(self, value):
if value <= 0:
raise ValueError("Width must be a positive number.")
self._width = value
# height에 대한 getter
@property
def height(self):
return self._height
# height에 대한 setter
@height.setter
def height(self, value):
if value <= 0:
raise ValueError("Height must be a positive number.")
self._height = value
# area는 읽기 전용 속성으로 설정 (getter만 정의)
@property
def area(self):
return self._width * self._height
# Rectangle 객체 생성
rect = Rectangle(5, 10)
# width와 height를 통해 값에 접근
print(rect.width) # 출력: 5
print(rect.height) # 출력: 10
# area 속성에 접근 (읽기 전용 속성)
print(rect.area) # 출력: 50
# 속성 값을 변경
rect.width = 20
rect.height = 15
print(rect.area) # 출력: 300
# 잘못된 값 설정 시 오류 발생
try:
rect.width = -5
except ValueError as e:
print(e) # 출력: Width must be a positive number.
위의 예시에서는 width, height, area 모두 @property 데코레이터를 사용함으로써 getter method를 정의해주었습니다(따로 getter라는 이름을 쓰진 않습니다). 그런데, width와 height는 이후 각각의 property에 대한 setter method도 추가해주었고, area는 setter가 따로 없습니다.
이런식으로 프로그래밍하면 어떤 점이 달라질까요? 일단 속성의 값을 설정할 때 겉으로 보기에는 직접적으로 접근해서 바꿔주는 것처럼 보입니다.
rect.width = 20
rect.height = 15
하지만 내부적으로는 method가 호출되어 작동되고 있죠. 내부 method에 너비가 음수면 error메시지가 나오도록 기능도 심어놓아서 만약 값을 -5로 설정하려고 시도한다면 에러메시지를 출력합니다. width와 height에 연동되어 있는 area도 width, height setter의 작동(width-->20, height-->15)과 함께 알아서 잘 업데이트되어 있습니다.
그리고, setter method가 없는 area를 직접적으로 접근해서 수정하려고 시도하면 어떻게 될까요?
rect.area = 50
AttributeError: property 'area' of 'Rectangle' object has no setter
와 같이 에러메시지가 나오는 것을 확인하실 수 있습니다. 이 녀석은 setter가 없는 property라서 getter를 통해서 읽기만 가능하고 쓰는것은 불가능하게 되는 것이죠! 제가 말씀드린 것처럼 한편의 잘 정돈된 글을 읽은 것 같지 않나요? pythonic programming은 저도 계속 말씀드리다시피 잘 적용을 못하는 부분인데, 계속 염두해 두면서 논리적으로 코딩하는 방법을 갈고 닦아야 할 것 같습니다.