파이썬으로 코딩을 하면서 데이터를 담아두기 위해 여러가지 방법을 사용한다.
리스트, 튜블, 딕셔너리, 네임드 튜플, 세트, 프로즌 세트 등등..
반면 클래스를 통해 데이터를 담아두면 type-safe해지기 때문에 프로그램 실행 중 오류가 발생할 확률이 적어진다는 장점이 있다.
파이썬 3.7에서 dataclass라는 모듈이 표준 라이브러리에 추가되었다. 이를 알아보고자 한다.
공식문서
dataclasses - Data Classes - Python 3.9.5 documentation
기존의 클래스 방식
dataclass가 등장하기 전 어떻게 데이터를 담아두기 위해 클래스를 작성하였는지 보자.
from datetime import date
class User:
def __init__(
self, id: int, name: str, birthdate: date, admin: bool = False):
self.id = id
self.name = name
self.birthdate = birthdate
self.admin = admin
위를 보면 id, name, birthdate, admin이 3번씩 반복되는 것을 알 수 있다. 이런 코드를 보일러 플레이드(boiler-plate)라고 한다. 필드 개수가 많은 클래스라면 이런 코드를 작성하는 것이 매우 복잡하고 어렵고, 오타라도 나면 버그로 이어질 수 있다.
또한 위 클래스를 인스턴스로 출력해 보면 출력 결과에 필드값이 나타나지를 않는다.
>>> user = User(id=1, name="Steve Jobs", birthdate=date(1955, 2, 24))
>>> user
<__main__.User object at 0x105558100>
출력이 되도록 __reper__() 메서드를 추가하여 출력되도록 바꿔본다.
from datetime import date
class User:
def __init__(
self, id: int, name: str, birthdate: date, admin: bool = False
) -> None:
self.id = id
self.name = name
self.birthdate = birthdate
self.admin = admin
def __repr__(self):
return (
self.__class__.__qualname__ + f"(id={self.id!r}, name={self.name!r}, "
f"birthdate={self.birthdate!r}, admin={self.admin!r})")
>>> user1 = User(id=1, name="Steve Jobs", birthdate=date(1955, 2, 24))
>>> user1
User(id=1, name='Steve Jobs', birthdate=datetime.date(1955, 2, 24), admin=False)
두 개의 인스턴스간의 필드값이 모두 같을 때 동등한 인스턴스로 취급하고 십다면 __eq__() 메서드를 구현해 줘야 한다.
from datetime import date
class User:
def __init__(
self, id: int, name: str, birthdate: date, admin: bool = False
) -> None:
self.id = id
self.name = name
self.birthdate = birthdate
self.admin = admin
def __repr__(self):
return (
self.__class__.__qualname__ + f"(id={self.id!r}, name={self.name!r}, "
f"birthdate={self.birthdate!r}, admin={self.admin!r})"
)
def __eq__(self, other):
if other.__class__ is self.__class__:
return (self.id, self.name, self.birthdate, self.admin) == (
other.id,
other.name,
other.birthdate,
other.admin,
)
return NotImplemented
>>> user1 = User(id=1, name="Steve Jobs", birthdate=date(1955, 2, 24))
>>> user2 = User(id=1, name="Steve Jobs", birthdate=date(1955, 2, 24))
>>> user1 == user2
True
데이터클래스 작성하기
데이터 클래스 모듈은 위와 같이 데이터를 담아두기 위한 클래스를 매우 적은 양의 코드로 작성하게 해 준다.
from dataclasses import dataclass
from datetime import date
@dataclass
class User:
id: int
name: str
birthdate: date
admin: bool = False
이렇게만 작성하면 위의 작성했던 메소드들이 전부 자동으로 생성이 된다.
만약 데이터의 불변성이 보장되어야하는 경우라면 frozen
옵션을 사용하면 된다.
@dataclass(frozen=True)
데이터 대소 비교 및 정렬
데이터 클래스의 인스턴스 간에 대소비교를 하려고 하면 에러가 발생하는데 order
옵션을 주면 대소 비교를 사용할 수 있다.
from dataclasses import dataclass
from datetime import date
@dataclass(order=True)
class User:
id: int
name: str
birthdate: date
admin: bool = False
>>> user1 = User(id=1, name="Steve Jobs", birthdate=date(1955, 2, 24))
>>> user2 = User(id=2, name="Bill Gates", birthdate=date(1955, 10, 28))
>>> user1 < user2
True
>>> user1 > user2
False
>>> sorted([user2, user1])
[User(id=1, name='Steve Jobs', birthdate=datetime.date(1955, 2, 24), admin=False), User(id=2, name='Bill Gates', birthdate=datetime.date(1955, 10, 28), admin=False)]
셋트나 딕셔너리 사용하기
데이터 클래스의 인스턴스는 기본적으로 hashable 하지 않기에 세트(set)의 값이나 사전(dictionary)의 키로 사용을 할 수가 없다.
>>> set([user1, user2])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'User'
데이터클래스의 인스턴스를 hashable하게 만들고 싶다면 unsage_hash
옵션을 사용하면 된다.
from dataclasses import dataclass
from datetime import date
@dataclass(unsafe_hash=True)
class User:
id: int
name: str
birthdate: date
admin: bool = False
세트를 이용해 중복 데이터를 제거할 수 있다.
>>> user1 = User(id=1, name="Steve Jobs", birthdate=date(1955, 2, 24))
>>> user2 = User(id=2, name="Bill Gates", birthdate=date(1955, 10, 28))
>>> user3 = User(id=1, name="Steve Jobs", birthdate=date(1955, 2, 24))
>>> user4 = User(id=2, name="Bill Gates", birthdate=date(1955, 10, 28))
>>> set([user1, user2, user3, user4]){User(id=2, name='Bill Gates', birthdate=datetime.date(1955, 10, 28), admin=False), User(id=1, name='Steve Jobs', birthdate=datetime.date(1955, 2, 24), admin=False)}
데이터클래스 사용시 주의사항
데이터 클래스를 사용할 때 흔히 나오는 실수는 list와 같은 가변 데이터 타입의 필드에 기본값을 할당해 줄 때 발생한다.
from dataclasses import dataclass
from datetime import date
from typing import List
@dataclass(unsafe_hash=True)
class User:
id: int
name: str
birthdate: date
admin: bool = False
friends: List[int] = []
필드의 기본값은 인스턴스 간에 공유가 되기 때문에 기본값 할당이 허용되지 않는다.
이럴 때는 filed
함수의 default_factory
옵션을 사용해서 매번 새로운 리스트가 생성될 수 있도록 해줘야 한다.
from dataclasses import dataclass, field
from datetime import date
from typing import List
@dataclass(unsafe_hash=True)
class User:
id: int
name: str
birthdate: date
admin: bool = False
friends: List[int] = field(default_factory=list)
'study > Python 🌼' 카테고리의 다른 글
[Python] 월 단위의 날짜 차이 계산 (0) | 2021.07.05 |
---|---|
[Python] Mac SSLCertVerificationError (0) | 2021.06.30 |
[Python] SQLAlcehmy (0) | 2021.05.28 |
[Python] 암호화를 하는 bcrypt (0) | 2021.05.28 |
[Python] classmethod vs staticmethod (0) | 2021.05.28 |