본문 바로가기
Python

property (함수/데코레이터)[파이썬/python]

by Deeppago 2022. 2. 17.
-목차-

1. 필드명

2.  Getter/Setter

3. Property() 함수

4. @Property 데코레이터

5. @Property 데코레이터 활용

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'

 


참고 자료

https://www.daleseo.com/python-property/

'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

댓글