본문 바로가기
Coding/Python

Python dataclass property/setter

by Hide­ 2019. 9. 18.
반응형

파이썬 3.7부터 보다 간편하게 클래스를 생성할 수 있는 dataclass가 추가됐다. dataclass를 사용하여 Getter/Setter를 추가할 때 발생한 문제점과 해결 방법에 대해 기술한다.

파이썬에서 Getter/Setter를 사용하려면 @proprety와 @요소.setter라는 특수한 데코레이터를 사용해야한다. 먼저 일반적인 클래스의 경우 어떻게 사용하는지부터 살펴본다.

class User:
    def __init__(self, id: str, pw: str):
        self.id = id
        self._pw = pw

    @property
    def pw(self):
        return self._pw

    @pw.setter
    def pw(self, value: str):
        self._pw = value


if __name__ == '__main__':
    user = User(id='hide', pw='hide123')
    print(user.__dict__)


Output: {'id': 'hide', '_pw': 'hide123'}

위처럼 정의해주면 실제 내부적으로는 _pw 라는 변수를 사용했지만 클래스를 생성할 때 pw라는 변수를 통해 값을 할당해줄 수 있다. 만약 위의 방식이 아닌 새롭게 추가된 dataclass를 통해 클래스를 생성해도 동일하게 동작할까?

from dataclasses import dataclass


@dataclass
class User:
    id: str
    _pw:str

    @property
    def pw(self):
        return self._pw

    @pw.setter
    def pw(self, value: str):
        self._pw = value


if __name__ == '__main__':
    user = User(id='hide', pw='hide123')
    print(user.__dict__)


Output: NameError: name 'pw' is not defined

코드의 결과에도 명시해놨지만 pw가 정의되지 않았다는 오류가 발생한다. 위 문제를 해결하려면 약간의 트릭이 필요하다.

from dataclasses import dataclass, field


@dataclass
class User:
    id: str
    pw: str
    _pw:str = field(init=False)

    @property
    def pw(self):
        return self._pw

    @pw.setter
    def pw(self, value: str):
        self._pw = value


if __name__ == '__main__':
    user = User(id='hide', pw='hide123')
    print(user.__dict__)


Output: {'id': 'hide', '_pw': 'hide123'}

위처럼 실제로 pw라는 변수를 추가해주고 _pw변수에 대해서는 field(init=False)옵션을 주게되면 dataclass에 의해 생성자가 생성될 때 _pw를 생성하지 않는다. 주의할점은, 반드시 setter를 구현해야한다는 것이다. 만약 getter만 구현한다면 오류가 발생할 것이다.

참고 : https://blog.florimond.dev/reconciling-dataclasses-and-properties-in-python