본문 바로가기
C.W.K.
Stream
Lesson 06 of 07 · published

dataclass — 데이터 객체의 반복 코드를 맡기기

~22 min · dataclass, frozen, kw_only, field, post_init

Level 0호기심
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete

필드 선언에서 반복 메서드를 만든다

@dataclass는 어노테이션된 필드로 __init__, repr, eq 같은 데이터 객체의 기본 메서드를 만들어. 상태를 담는 레코드라면 손으로 같은 코드를 되풀이할 필요가 없어.

옵션은 객체 계약을 드러내

frozen은 필드 재할당을 막고, kw_only는 이름으로만 생성하게 하며, order는 필드 순서로 비교를 만들어. field(default_factory=list)는 인스턴스마다 새 가변값을 줘.

__post_init__에서 검증하고 파생값을 만든다

자동 초기화 뒤에 실행되므로 필드 사이 조건을 확인하거나 계산된 값을 세울 수 있어. 행동과 수명 규칙이 많아지면 평범한 클래스로 돌아가는 편이 더 솔직해.

Code

손으로 쓴 데이터 클래스와 @dataclass 비교·python
from dataclasses import dataclass

# 손으로
class PointManual:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return f"PointManual(x={self.x}, y={self.y})"

    def __eq__(self, other):
        return isinstance(other, PointManual) and self.x == other.x and self.y == other.y

# Dataclass
@dataclass
class Point:
    x: int
    y: int

p1 = Point(3, 4)
p2 = Point(3, 4)
print(p1)              # Point(x=3, y=4)
print(p1 == p2)        # True
frozen·kw_only·order 옵션·python
from dataclasses import dataclass

@dataclass(frozen=True)
class Coordinate:
    lat: float
    lon: float

c = Coordinate(37.5, 127.0)
try:
    c.lat = 40.0          # frozen — 할당 raise
except Exception as e:
    print(type(e).__name__, e)

# frozen 은 무료로 __hash__ 도
print({c, Coordinate(37.5, 127.0)})    # 한 원소 set (dedup)

@dataclass(kw_only=True)
class Config:
    host: str
    port: int = 8000
    debug: bool = False

# 키워드 인자 필수
cfg = Config(host="localhost", debug=True)
try:
    Config("localhost", 9000)         # positional 거부
except TypeError as e:
    print(e)
field와 default_factory·python
from dataclasses import dataclass, field

@dataclass
class User:
    name: str
    tags: list[str] = field(default_factory=list)    # 인스턴스마다 새 list
    password_hash: str = field(repr=False, default="") # __repr__ 에서 제외
    id: int = field(init=False, default=0)            # __init__ 인자에서 제외

u1 = User("alice")
u1.tags.append("admin")
u2 = User("bob")
print(u1.tags)         # ['admin']
print(u2.tags)         # []          — 독립
print(u1)              # User(name='alice', tags=['admin'], id=0)   — password_hash 숨김
__post_init__ 검증과 파생 필드·python
from dataclasses import dataclass, field

@dataclass
class Range:
    start: int
    end: int
    length: int = field(init=False)             # __init__에서 받지 않고 계산

    def __post_init__(self):
        if self.end < self.start:
            raise ValueError("end >= start 여야")
        self.length = self.end - self.start

r = Range(start=10, end=25)
print(r)                  # Range(start=10, end=25, length=15)

try:
    Range(start=10, end=5)
except ValueError as e:
    print(e)
dataclass가 맞지 않는 행동 중심 객체·python
# dataclass 는 *값* 용 (자동 __init__/__repr__/__eq__ 가진 데이터).
# 풍부한 동작 있으면 일반 클래스.

class Connection:               # 동작 많음 — 좋은 dataclass 후보가 아님
    def __init__(self, host):
        self.host = host
        self._socket = None

    def connect(self):
        # 복잡한 setup
        self._socket = f"<socket to {self.host}>"

    def send(self, data):
        if self._socket is None:
            raise RuntimeError("연결되지 않음")
        return f"sent {data!r} via {self._socket}"

# 이거 dataclass 로 하려면 post_init + field(init=False) 잔뜩 — 직접 쓰는 게 명확.

External links

Exercise

id, items, total을 가진 Order dataclass를 만들고 items에는 default_factory를 써. ClassVar 가격표를 바탕으로 __post_init__에서 total을 다시 계산해. frozen=True, kw_only=True인 FrozenOrder도 만들어 불변성과 set 사용 가능 여부를 확인해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.