-목차-
1. 필드명
사람의 이름, 성, 나이 데이터를 담기 위한 간단한 클래스를 하나 작성해보자. 아래 Person 클래스는 이름 first_name, last_name, age 이렇게 3개의 필드로 이루어져 있다.
class Person:
def __init__(self, first_name, last_name, age):
self.first_name = first_name
self.last_name = last_name
self.age = age
Person 클래스의 인스턴스를 생성 후에, 현재 필드 값을 읽거나 새로운 필드 값 쓰는 것은 매우 자유롭다.
>>> person = Person("John", "Doe", 20)
>>> person.age
20
>>> person.age = person.age + 1
>>> person.age
21
이렇게 필드명을 사용해서 객체의 내부 데이터에 접근하는 것은 편리하지만, 해당 데이터는 외부로 부터 무방비 상태에 놓이게 된다.
2. Getter/Setter
클래스 인스턴스의 내부 데이터를 보호하기 위해서 데이터의 접근용 메서드를 작성하는 것은 객체 지향 프로그래밍에서 흔히 볼 수 있는 패턴이다. 일반적으로 데이터를 읽어주는 메서드를 getter(게터), 데이터를 변경해주는 메서드를 setter(세터)라고 한다.
Person 클래스에 age 필드에 대한 get_age()와 set_age() 메서드를 추가해보자. 나이는 음수가 될 수 없으므로 set_age() 메서드에 음수가 인자로 넘어오면 예외가 발생하도록 하였다.
class Person:
def __init__(self, first_name, last_name, age):
self.first_name = first_name
self.last_name = last_name
self.set_age(age)
def get_age(self):
return self._age
def set_age(self, age):
if age < 0:
raise ValueError("Invalid age")
self._age = age
여기서 놓치기 쉬운 부분은 _로 시작하는 이름을 가진 변수는 외부에서 직접 접근하지 않는 파이썬의 관행에 따라, 인스턴스 변수명을 age 대신에 _age로 변경하였다는 것이다.
>>> person = Person("John", "Doe", 20)
>>> person.get_age()
20
>>> person.set_age(-1)
ValueError: Invalid age
>>> person.set_age(person.get_age() + 1)
>>> person.get_age()
21
이제 Person 클래스의 인스턴스에 저장되어 있는 나이 데이터에 접근하거나 변경하려면 메서드를 이용해야 한다.
getter/setter 메서드를 통해서 객체의 내부 데이터에 대한 접근을 좀 더 통제할 수 있게되었지만 기존에 필드명을 바로 사용할 때 보다는 코드가 조금 지저분해졌다.
3. Property() 함수
파이썬의 내장 함수인 property()를 사용하면 마치 필드명을 사용하는 것처럼 깔끔하게 getter/setter 메서드가 호출되게 할 수 있다.
class Person:
def __init__(self, first_name, last_name, age):
self.first_name = first_name
self.last_name = last_name
self.age = age
def get_age(self):
return self._age
def set_age(self, age):
if age < 0:
raise ValueError("Invalid age")
self._age = age
age = property(get_age, set_age)
property() 함수의 첫 번째 인자로 getter 메서드를 두 번째 인자로 setter 메서드를 넘겨주면 age라는 필드명을 이용해서 다시 나이 데이터에 접근할 수 있게 된다.
>>> person = Person("John", "Doe", 20)
>>> person.age
20
>>> person.age = -1
ValueError: Invalid age
>>> person.age = person.age + 1
>>> person.age
21
클래스를 사용하는 측면에서는 일반 필드에 접근하는 것처럼 보이지만 내부적으로 getter와 setter 메서드가 호출 된다. 따라서 나이를 음수로 변경하려고 하면 set_age() 메서드를 직접 호출하는 것과 동일하게 예외가 발생한다.
4. @Property 데코레이터
파이썬의 내장 데코레이터인 @property를 사용하면 위와 동일하게 작동하는 코드를 좀 더 간결하고 읽기 편하게 작성할 수 있다.
class Person:
def __init__(self, first_name, last_name, age):
self.first_name = first_name
self.last_name = last_name
self.age = age
@property
def age(self):
return self._age
@age.setter
def age(self, age):
if age < 0:
raise ValueError("Invalid age")
self._age = age
기존의 getter 메서드 위에 @property 데코레이터를 선언하고, 메서드 이름으로부터 get_을 삭제한다. setter 메서드의 경우에는 @<필드명>.setter 데코레이터를 선언하고, 메서드 이름으로부터 set_을 삭제한다.
>>> person = Person("John", "Doe", 20)
>>> person.age
20
>>> person.age = -1
ValueError: Invalid age
>>> person.age = person.age + 1
>>> person.age
21
property 함수나 @property 데코레이터를 사용했을 때 가장 큰 이점은 외부에 티 내지 않고 내부적으로 클래스의 필드 접근 방법을 바꿀 수 있다는 것이다. Person 클래스를 사용하는 관점에서 봤을 때 나이 데이터는 항상 age라는 필드명으로 접근하고 변경할 수 있다는 사실은 변하지 않기 때문이다.
5. @Property 데코레이터 활용
클래스를 작성하다보면 다른 필드로 부터 값이 유추되는 읽기 전용 필드가 필요할 때가 있다. 예를 들어, Person 클래스에서 전체 이름을 얻고 싶다면 다음과 같이 @property 데코레이터를 이용해서 full_name 필드를 추가해줄 수 있다.
class Person:
def __init__(self, first_name, last_name, age):
self.first_name = first_name
self.last_name = last_name
self.age = age
@property
def full_name(self):
return self.first_name + " " + self.last_name
>>> person = Person("John", "Doe", 20)
>>> person.full_name
'John Doe'
참고 자료
'Python' 카테고리의 다른 글
Passed-By-Assignment (0) | 2022.02.18 |
---|---|
Duck Typing (0) | 2022.02.17 |
메모리 누수[파이썬/python] (0) | 2022.02.17 |
GC(Garbage Collection) (0) | 2022.02.16 |
GIL(Global Interpreter Lock) (0) | 2022.02.16 |
댓글