프로그래밍/파이썬
파이썬(python) dataclass 사용/활용 예제
dang_dang
2025. 3. 15. 10:45
728x90
- 기본 dataclass - Point 클래스
- 기본값 설정 - 생성자에서 기본값을 사용하는 방법
- field() 함수 활용 - 복잡한 기본값 설정 및 속성 제어
- default_factory로 가변 객체 기본값 설정
- repr, compare 등 필드 동작 제어
- 초기화 후 처리 - __post_init__와 InitVar 활용법
- 불변 객체 생성 - frozen=True 옵션으로 불변 dataclass 생성
- 상속 구현 - dataclass 간 상속 관계 설정
- 클래스 변수 사용 - ClassVar를 통한 인스턴스 간 공유 변수 설정
- JSON 변환 - dataclass 객체의 직렬화/역직렬화
- 동적 dataclass 생성 - 런타임에 dataclass 동적 생성하기
- 복잡한 실제 사례 - 주문 시스템 구현 예제
dataclass를 사용하면 다음과 같은 장점이 있음.
- __init__, __repr__, __eq__ 등의 메서드를 자동으로 생성
- 타입 힌팅을 통한 더 명확한 코드 작성
- 상속, 불변성 등 객체지향 디자인 패턴 적용 용이
- 데이터 처리와 직렬화/역직렬화 간소화
from dataclasses import dataclass, field, InitVar, make_dataclass, asdict, astuple, replace
from typing import List, Dict, Optional, ClassVar, Any
import json
from datetime import datetime
# 1. 기본 dataclass 예제
@dataclass
class Point:
x: int
y: int
def __str__(self):
return f"{self.x}, {self.y}"
# 2. 기본값 설정 예제
@dataclass
class Rectangle:
width: int
height: int = 10 # 기본값 설정
color: str = "white"
def area(self):
return self.width * self.height
# 3. field() 함수를 사용한 고급 설정
@dataclass
class Product:
name: str
price: float
# default_factory를 사용하여 가변 기본값 설정
tags: List[str] = field(default_factory=list)
# repr=False로 설정하여 repr 출력에서 제외
internal_id: str = field(default="", repr=False)
# compare=False로 설정하여 객체 비교에서 제외
timestamp: datetime = field(
default_factory=datetime.now,
compare=False
)
def is_expensive(self):
return self.price > 100
# 4. 초기화 후 처리 및 InitVar 사용
@dataclass
class Circle:
radius: float
diameter: InitVar[Optional[float]] = None # 초기화에만 사용되는 변수
area: float = field(init=False) # 초기화에서 제외
def __post_init__(self, diameter):
# diameter가 제공되면 radius 계산
if diameter is not None:
self.radius = diameter / 2
# area 계산
self.area = 3.14159 * (self.radius ** 2)
# 5. 불변 dataclass (frozen=True)
@dataclass(frozen=True)
class ImmutablePoint:
x: int
y: int
def distance_from_origin(self):
return (self.x ** 2 + self.y ** 2) ** 0.5
# 6. 상속을 사용한 dataclass
@dataclass
class Person:
name: str
age: int
def is_adult(self):
return self.age >= 18
@dataclass
class Employee(Person):
company: str
salary: float = 0
def annual_salary(self):
return self.salary * 12
# 7. ClassVar 사용 예제
@dataclass
class Config:
# 클래스 변수 (인스턴스마다 다르지 않음)
VERSION: ClassVar[str] = "1.0.0"
DEBUG: ClassVar[bool] = False
# 인스턴스 변수
user_name: str
api_key: str
# 8. JSON 변환 예제
@dataclass
class User:
id: int
name: str
email: str
active: bool = True
permissions: List[str] = field(default_factory=list)
def to_json(self):
return json.dumps(asdict(self))
@classmethod
def from_json(cls, json_str):
return cls(**json.loads(json_str))
# 9. 동적으로 dataclass 생성
Fields = [
('name', str),
('age', int, field(default=0)),
('email', str, field(default=''))
]
# 동적으로 dataclass 생성
DynamicPerson = make_dataclass('DynamicPerson', Fields)
# 10. 복잡한 예제: 주문 시스템
@dataclass
class OrderItem:
product_id: int
name: str
price: float
quantity: int = 1
@property
def total(self):
return self.price * self.quantity
@dataclass
class Order:
id: int
customer_name: str
items: List[OrderItem] = field(default_factory=list)
created_at: datetime = field(default_factory=datetime.now)
processed: bool = False
@property
def total_amount(self):
return sum(item.total for item in self.items)
def add_item(self, item):
self.items.append(item)
def to_dict(self):
return {
'id': self.id,
'customer_name': self.customer_name,
'items': [asdict(item) for item in self.items],
'created_at': self.created_at.isoformat(),
'processed': self.processed,
'total_amount': self.total_amount
}
# 테스트 및 사용 예제
def run_examples():
print("\n===== 1. 기본 예제 =====")
p1 = Point(100, 200)
p2 = Point(100, 200)
p3 = Point(1, 2)
print(f"p1: {p1}")
print(f"x={p1.x}, y={p1.y}")
print(f"p1 == p2: {p1 == p2}") # True (자동 생성된 __eq__)
print(f"p1 != p3: {p1 != p3}") # True
print("\n===== 2. 기본값 설정 =====")
r1 = Rectangle(5) # height는 기본값 사용
r2 = Rectangle(5, 20, "blue")
print(f"r1: {r1}, 넓이: {r1.area()}")
print(f"r2: {r2}, 넓이: {r2.area()}")
print("\n===== 3. field() 함수 사용 =====")
prod1 = Product("노트북", 1200.0, ["전자제품", "컴퓨터"])
prod2 = Product("마우스", 25.0)
prod2.tags.append("주변기기")
print(f"prod1: {prod1}") # internal_id는 repr에서 제외됨
print(f"prod2: {prod2}")
print(f"prod1은 비싼가?: {prod1.is_expensive()}")
print(f"prod2는 비싼가?: {prod2.is_expensive()}")
# 동일한 tags 리스트를 공유하지 않음 (default_factory 덕분)
print(f"prod1.tags != prod2.tags: {prod1.tags != prod2.tags}")
print("\n===== 4. 초기화 후 처리 =====")
c1 = Circle(5.0)
c2 = Circle(radius=0, diameter=10.0) # diameter로부터 radius 계산
print(f"c1: {c1}")
print(f"c2: {c2}")
print("\n===== 5. 불변 dataclass =====")
ip = ImmutablePoint(3, 4)
print(f"ip: {ip}")
print(f"원점까지의 거리: {ip.distance_from_origin()}")
try:
ip.x = 10 # 에러 발생: frozen=True이므로 수정 불가
except Exception as e:
print(f"불변 객체 수정 시도 시 에러: {type(e).__name__}")
# replace() 함수로 불변 객체의 수정된 복사본 생성
ip2 = replace(ip, x=10)
print(f"replace로 새 객체 생성: {ip2}")
print("\n===== 6. 상속 =====")
emp = Employee("홍길동", 30, "ABC주식회사", 5000000)
print(f"직원: {emp}")
print(f"성인 여부: {emp.is_adult()}")
print(f"연봉: {emp.annual_salary()}원")
print("\n===== 7. ClassVar 사용 =====")
config = Config("user123", "api_key_123")
print(f"config: {config}")
print(f"버전: {Config.VERSION}")
print(f"디버그 모드: {Config.DEBUG}")
print("\n===== 8. JSON 변환 =====")
user = User(1, "김철수", "kim@example.com", permissions=["read", "write"])
user_json = user.to_json()
print(f"JSON: {user_json}")
user2 = User.from_json(user_json)
print(f"JSON에서 복원: {user2}")
print("\n===== 9. 동적 dataclass =====")
dp = DynamicPerson("이영희", 25, "lee@example.com")
print(f"동적 생성 객체: {dp}")
print("\n===== 10. 복잡한 예제: 주문 시스템 =====")
order = Order(1, "박지성")
order.add_item(OrderItem(101, "키보드", 50000, 1))
order.add_item(OrderItem(102, "마우스", 30000, 2))
print(f"주문: {order}")
print(f"총 금액: {order.total_amount}원")
print(f"주문 정보(dict): {order.to_dict()}")
# asdict, astuple 함수 사용
print("\n===== 추가: asdict, astuple 함수 =====")
point_dict = asdict(p1)
point_tuple = astuple(p1)
print(f"asdict(): {point_dict}")
print(f"astuple(): {point_tuple}")
if __name__ == "__main__":
run_examples()
728x90