@dataclass, field

Leejaegun·2025년 4월 22일

Python & etc

목록 보기
23/27

1️⃣ 왜 @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=listA()가 호출될 때마다 새로운 리스트를 만들어 줌

🎯 질문 코드 해석

reward_funcs: list[str] = field(
    default_factory=lambda: ["accuracy", "format"],  # 기본값 함수
    metadata={"help": "List of reward functions. Possible values: 'accuracy', 'format'"},  # 도움말 설명
)

이건 다음을 의미해요:

  • reward_funcs는 리스트 타입이고
  • 초기값으로 ["accuracy", "format"]이 설정되며
  • 문서화 또는 CLI 파싱을 위한 설명이 함께 달림

📦 요약

문법설명
= 1정적 값은 그냥 대입 가능
field(default=...)기본값을 명시적으로 지정
field(default_factory=...)리스트/딕셔너리처럼 가변 객체는 이걸로!
field(metadata={...})도움말 등 부가 정보를 저장 (예: HuggingFace CLI 자동화에 활용됨)

좋아요! 지금 질문의 핵심은 다음과 같습니다:

field()를 쓰면 뭐가 특별해지고, default_factory는 정확히 어떻게 동작하냐?”


✅ 상황 비교: field() 없이 vs field() 사용

📌 1. 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'] ← ❗ 같이 바뀜!

✅ 2. 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_factorymutable default (list, dict, set 등)에만 사용하는 게 핵심입니다.

필요하면 아래처럼도 씁니다:

field(default_factory=dict)
field(default_factory=set)
field(default_factory=list)
profile
Lee_AA

0개의 댓글