Python dataclassesで
楽にクラス定義!実践ガイド
dataclassesモジュールを使えば、ボイラープレートコードを大幅に削減してクラスを定義できます。
こんな人向けの記事です
- Pythonのクラス定義を簡潔にしたい人
- __init__や__repr__を毎回書くのが面倒な人
- 型ヒントを活用したコードを書きたい人
Step 1dataclassの基本
@dataclassデコレータを使うと、__init__、__repr__、__eq__が自動生成されます。
Python
from dataclasses import dataclass
@dataclass
class User:
name: str
age: int
email: str
# 自動生成された__init__で初期化
user = User(name="田中太郎", age=30, email="tanaka@example.com")
print(user)
# User(name='田中太郎', age=30, email='tanaka@example.com')
# __eq__も自動生成
user2 = User(name="田中太郎", age=30, email="tanaka@example.com")
print(user == user2) # True自動生成されるメソッド
__init__(初期化)、__repr__(文字列表現)、__eq__(等価比較)がデフォルトで自動生成されます。Step 2デフォルト値とfield()
フィールドにデフォルト値を設定できます。ミュータブルなデフォルト値にはfield(default_factory=...)を使います。
Python
from dataclasses import dataclass, field
from typing import List
@dataclass
class Team:
name: str
leader: str = "未定" # 単純なデフォルト値
members: List[str] = field(default_factory=list) # ミュータブルはfieldで
_score: int = field(default=0, repr=False) # reprから除外
team1 = Team(name="開発チーム")
team1.members.append("Alice")
print(team1)
# Team(name='開発チーム', leader='未定', members=['Alice'])
team2 = Team(name="営業チーム")
print(team2.members) # [](team1とは独立)ミュータブルなデフォルト値に注意
members: list = []と書くとエラーになります。必ずfield(default_factory=list)を使いましょう。Step 3__post_init__で初期化後処理
__post_init__メソッドで、__init__完了後に追加の初期化処理を実行できます。
Python
from dataclasses import dataclass
@dataclass
class Rectangle:
width: float
height: float
area: float = 0 # __post_init__で計算
def __post_init__(self):
self.area = self.width * self.height
if self.width < 0 or self.height < 0:
raise ValueError("幅と高さは正の値である必要があります")
rect = Rectangle(width=10, height=5)
print(rect.area) # 50.0
# バリデーションも可能
# Rectangle(width=-1, height=5) # ValueErrorStep 4frozen(イミュータブル)
frozen=Trueにすると、インスタンス作成後にフィールドを変更できなくなります。
Python
from dataclasses import dataclass
@dataclass(frozen=True)
class Point:
x: float
y: float
p = Point(x=1.0, y=2.0)
print(p) # Point(x=1.0, y=2.0)
# p.x = 3.0 # FrozenInstanceError!
# frozenなdataclassはハッシュ可能(dictのキーやsetに使える)
points = {Point(0, 0): "原点", Point(1, 1): "右上"}
print(points[Point(0, 0)]) # 原点Step 5比較と並び替え
order=Trueにすると、比較演算子(<, <=, >, >=)が自動生成されます。
Python
from dataclasses import dataclass
@dataclass(order=True)
class Student:
score: int
name: str
students = [
Student(85, "Alice"),
Student(92, "Bob"),
Student(78, "Charlie"),
]
# score → name の順で比較
for s in sorted(students):
print(f"{s.name}: {s.score}")
# Charlie: 78
# Alice: 85
# Bob: 92Step 6通常クラスとの比較
dataclassを使わない場合と比べて、コード量が大幅に削減できます。
通常のクラス(ボイラープレートが多い)
class Product:
def __init__(self, name, price, stock=0):
self.name = name
self.price = price
self.stock = stock
def __repr__(self):
return f"Product(name={self.name!r}, price={self.price!r}, stock={self.stock!r})"
def __eq__(self, other):
if not isinstance(other, Product):
return NotImplemented
return (self.name, self.price, self.stock) == (other.name, other.price, other.stock)dataclassを使う場合(簡潔!)
from dataclasses import dataclass
@dataclass
class Product:
name: str
price: int
stock: int = 0どちらを使うべき?
データを保持するのが主な目的のクラスには@dataclassが最適です。複雑なロジックを持つクラスには通常のクラス定義が適しています。まとめ
@dataclassで__init__、__repr__、__eq__を自動生成- ミュータブルなデフォルト値には
field(default_factory=...) __post_init__で初期化後のバリデーション・計算frozen=Trueでイミュータブルなクラスを作成order=Trueで比較・ソートが可能に