데이터 클래스는 데이터의 '틀'이 명확히 정해져 있을 경우 사용한다.
from dataclasses import dataclass
@dataclass
class UserData:
name: str
age: int
user = UserData(name="파이", age=25)
# 속성에 접근할 때 점(.)을 사용합니다. IDE가 자동완성을 완벽히 지원합니다.
print(user.name) # 출력: 파이
# 존재하지 않는 필드를 실수로 입력하면 개발 단계에서 에러를 잡아줍니다.
# user.email = "test@test.com" # 미리 정의하지 않았다면 정적 분석기에서 경고 발생
Mapping은 추상 기반 클래스(ABC)로, 이를 상속받으면 일반 딕셔너리처럼 getitem, iter, len 등을 구현하여 딕셔너리 인터페이스를 가진 커스텀 객체를 만들 수 있다.
from collections.abc import Mapping
class CustomUserDict(Mapping):
def __init__(self, data):
# 내부적으로 데이터를 딕셔너리 형태로 저장
self._data = data
# Mapping을 상속받으면 아래 3가지 매직 메서드를 반드시 구현해야 합니다.
def __getitem__(self, key):
return self._data[key]
def __iter__(self):
return iter(self._data)
def __len__(self):
return len(self._data)
# 객체 생성 (딕셔너리처럼 데이터를 넘김)
user_dict = CustomUserDict({"name": "파이", "age": 25})
# 데이터에 접근할 때 대괄호(['key'])를 사용합니다.
print(user_dict["name"]) # 출력: 파이
# 딕셔너리의 유용한 메서드들(keys(), values(), items(), get())을 자동으로 사용할 수 있습니다.
print(list(user_dict.keys())) # 출력: ['name', 'age']
다루려는 데이터의 필드(속성) 이름들이 고정되어 있을 때 (예: Id, Password, Email)
개발할 때 자동완성(Linting)과 타입 검사의 도움을 받아 오타를 줄이고 싶을 때
데이터 자체를 표현하는 독립된 객체(레코드)가 필요할 때
데이터의 키(Key) 개수나 이름이 동적으로 계속 변할 때
기존의 딕셔너리 기능(for k, v in obj.items(), obj.get('key') 등)을 그대로 유지하면서, 특정 키에 접근할 때 로깅을 남기거나 데이터를 가공하는 등의 추가 로직(Customizing)이 필요할 때
불변 딕셔너리(Read-only dictionary)를 안전하게 직접 구현하고 싶을 때
프로그래밍할 때 데이터의 구조가 명확하고 고정되어 있다면 코드가 간결하고 안전한 dataclass가 훨씬 유리하며, 딕셔너리 고유의 유연한 기능과 동적 데이터 핸들링이 필요하다면 Mapping을 커스텀하는 것이 맞다.