@dataclass에 대입하는 거냐? 진짜 그렇게 대단한 거냐?@dataclass는 진짜 미친듯이 편하게 많은 걸 자동으로 해줘서 그래요.
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def __repr__(self):
return f"Student(name={self.name}, age={self.age})"
def __eq__(self, other):
return isinstance(other, Student) and self.name == other.name and self.age == other.age
⬇️ 그런데 아래처럼 @dataclass 딱 붙이면…
from dataclasses import dataclass
@dataclass
class Student:
name: str
age: int
자동으로 아래를 해줍니다:
__init__() __repr__() __eq__() __hash__() (선택) __lt__(), __le__(), ... (order=True 옵션 시)📌 그래서 "값만 들고 있는 클래스"를 정의할 때, 10줄이 1줄로 줄어요.
→ 특히 config, argument, data 구조체 등에서 반복 작업을 획기적으로 줄이는 핵심 도구임.
결론:
dataclass는 클래스 정의를 훨씬 간결하고 안전하게 만들어주는 Python 핵심 문법 중 하나입니다. 실전에서 엄청 자주 써요.
(네, 사실상 "존나게 대단"합니다)
field()란?
dataclasses.field()는@dataclass의 필드 속성을 세밀하게 설정할 수 있는 함수입니다.
from dataclasses import dataclass, field
@dataclass
class MyConfig:
x: int = 1 # 그냥 기본값
y: list[int] = field(default_factory=list) # 리스트는 이렇게!
| 인자명 | 역할 |
|---|---|
default | 기본값 설정 (정적 값) |
default_factory | 함수로 기본값 생성 (리스트, 딕셔너리 등 mutable 객체에 사용) |
metadata | 부가 정보 저장 (예: help 메시지) |
init | __init__()에 포함할지 여부 |
repr | __repr__()에 포함할지 여부 |
compare | __eq__() 등에 비교에 사용할지 여부 |
list에는 default_factory를 써야 할까?# ❌ 위험한 코드
@dataclass
class A:
x: list = []
# 모든 인스턴스가 같은 list 객체를 공유하게 됨 (버그 위험!)
# ✅ 안전한 방식
@dataclass
class A:
x: list = field(default_factory=list)
default_factory=list는 A()가 호출될 때마다 새로운 리스트를 만들어 줌reward_funcs: list[str] = field(
default_factory=lambda: ["accuracy", "format"], # 기본값 함수
metadata={"help": "List of reward functions. Possible values: 'accuracy', 'format'"}, # 도움말 설명
)
이건 다음을 의미해요:
reward_funcs는 리스트 타입이고["accuracy", "format"]이 설정되며| 문법 | 설명 |
|---|---|
= 1 | 정적 값은 그냥 대입 가능 |
field(default=...) | 기본값을 명시적으로 지정 |
field(default_factory=...) | 리스트/딕셔너리처럼 가변 객체는 이걸로! |
field(metadata={...}) | 도움말 등 부가 정보를 저장 (예: HuggingFace CLI 자동화에 활용됨) |
좋아요! 지금 질문의 핵심은 다음과 같습니다:
“
field()를 쓰면 뭐가 특별해지고,default_factory는 정확히 어떻게 동작하냐?”
field() 없이 vs field() 사용field() 없이 썼을 때 (❌ 위험한 패턴)from dataclasses import dataclass
@dataclass
class Config:
reward_funcs: list[str] = ["accuracy", "format"]
cfg1 = Config()
cfg2 = Config()
cfg1.reward_funcs.append("length")
print(cfg1.reward_funcs) # ['accuracy', 'format', 'length']
print(cfg2.reward_funcs) # ['accuracy', 'format', 'length'] ← ❗ 같이 바뀜!
field(default_factory=...) 사용 (🎯 올바른 방식)from dataclasses import dataclass, field
@dataclass
class Config:
reward_funcs: list[str] = field(default_factory=lambda: ["accuracy", "format"])
cfg1 = Config()
cfg2 = Config()
cfg1.reward_funcs.append("length")
print(cfg1.reward_funcs) # ['accuracy', 'format', 'length']
print(cfg2.reward_funcs) # ['accuracy', 'format'] ← 안전!
🟢 default_factory=lambda: ...는 인스턴스를 만들 때마다 새 리스트 생성
→ 각 인스턴스가 자기만의 리스트를 가짐 → 안전하고 의도대로 동작
| 방법 | 동작 방식 | 문제 여부 |
|---|---|---|
reward_funcs: list[str] = ["accuracy", "format"] | 모든 인스턴스가 같은 리스트 공유 | ❌ 위험 |
reward_funcs: list[str] = field(default_factory=lambda: ["accuracy", "format"]) | 인스턴스마다 새로운 리스트 생성 | ✅ 안전 |
default_factory는 mutable default (list, dict, set 등)에만 사용하는 게 핵심입니다.
필요하면 아래처럼도 씁니다:
field(default_factory=dict)
field(default_factory=set)
field(default_factory=list)