Python @dataclassとは?クラスを短く書く方法を解説
Pythonの@dataclassは、データを持つクラスを短く書くためのデコレータです。
__init__・__repr__・__eq__を自動で作ってくれます。
@dataclassの基本的な使い方
from dataclasses import dataclass
@dataclass
class User:
name: str
age: int
u = User("佐藤", 28)
print(u)
print(u.name, u.age)
print(User("佐藤", 28) == User("佐藤", 28))
実行結果:
User(name='佐藤', age=28)
佐藤 28
True
3行書いただけで、初期化・表示・比較がすべて動いています。
手で書くとどうなるか
class User:
def __init__(self, name, age):
self.name = name
self.age = age
def __repr__(self):
return f"User(name={self.name!r}, age={self.age!r})"
def __eq__(self, other):
if not isinstance(other, User):
return NotImplemented
return (self.name, self.age) == (other.name, other.age)
同じことをするのに12行かかります。しかもフィールドを1つ足すたびに3か所を直す必要があります。
@dataclassなら1行足すだけです。修正漏れによるバグが起きません。
何が自動生成されるのか
from dataclasses import dataclass
@dataclass
class User:
name: str
age: int
print("__init__:", "__init__" in User.__dict__)
print("__repr__:", "__repr__" in User.__dict__)
print("__eq__ :", "__eq__" in User.__dict__)
print("__hash__:", User.__hash__)
実行結果:
__init__: True
__repr__: True
__eq__ : True
__hash__: None
__hash__がNoneになっている点に注目してください。__eq__を定義するとハッシュ不可になるというPythonの規則によるものです。
hash(u)
実行結果:
TypeError: unhashable type: 'User'
辞書のキーや集合の要素にしたいならfrozen=Trueが必要です。「Python dataclassのfrozen=Trueとは?変更できないクラスを作る方法」で解説しています。
型注釈は必須(ただし強制はされない)
from dataclasses import dataclass
@dataclass
class User:
name: str
age: int
u = User(123, "文字列")
print(u)
実行結果:
User(name=123, age='文字列')
型が逆でも通ります。Pythonは実行時に型を検査しないためです。
ただし注釈を書かないとフィールドとして認識されません。
@dataclass
class Bad:
name = "既定" # 注釈なし → ただのクラス変数
print(Bad())
実行結果:
Bad()
nameが消えました。: strのような注釈が必須です。型を決めたくないならtyping.Anyを使ってください。
型を実際に検査したいなら、型チェッカー(mypy)を併用します。詳しくは「Python typingモジュールとは?型ヒントの書き方を初心者向けに解説」を参照してください。
既定値は後ろにまとめる
from dataclasses import dataclass
@dataclass
class P:
name: str
age: int = 0
print(P("鈴木"))
実行結果:
P(name='鈴木', age=0)
既定値のあるフィールドの後ろに、既定値のないフィールドは置けません。
@dataclass
class Bad:
a: int = 0
b: str
実行結果:
TypeError: non-default argument 'b' follows default argument
通常の関数の引数と同じ規則です。
@dataclassのオプション
| オプション | 既定 | 効果 |
|---|---|---|
init |
True |
__init__を作る |
repr |
True |
__repr__を作る |
eq |
True |
__eq__を作る |
order |
False |
大小比較を作る |
frozen |
False |
変更を禁止する |
slots |
False |
__slots__を作る(3.10以降) |
kw_only |
False |
キーワード専用にする(3.10以降) |
@dataclass(frozen=True, order=True)
class Point:
x: int
y: int
__post_init__で初期化後の処理を書く
from dataclasses import dataclass, field
@dataclass
class Rect:
w: int
h: int
area: int = field(init=False)
def __post_init__(self):
self.area = self.w * self.h
print(Rect(3, 4))
実行結果:
Rect(w=3, h=4, area=12)
__init__の直後に呼ばれます。計算で求まる値や入力の検証を書く場所です。
def __post_init__(self):
if self.age < 0:
raise ValueError(f"年齢が不正です: {self.age}")
field(init=False)を付けたフィールドは__init__の引数から外れます。
他の選択肢との比較
| 方法 | 変更 | メソッド | 向いている場面 |
|---|---|---|---|
@dataclass |
できる | 持てる | データ+振る舞い |
NamedTuple |
できない | 持てる | 軽量・タプルとして扱う |
TypedDict |
できる | 持てない | JSONを辞書のまま扱う |
| 普通の辞書 | できる | 持てない | 構造が決まらない |
迷ったら@dataclassです。あとから振る舞いを足せるうえ、frozen=Trueで不変にもできます。
JSONを辞書のまま扱いたいなら「Python TypedDictとは?辞書のキーと型を定義する方法」、タプルとして扱いたいなら「【Python入門】namedtupleとは?使い方とメリットを初心者向けにわかりやすく解説」を参照してください。
@dataclassのまとめ
@dataclassは__init__・__repr__・__eq__を自動生成- 型注釈が必須(書かないとフィールドにならない)
- 型は実行時に検査されない——検査するなら型チェッカー
- 既定値のあるフィールドは後ろにまとめる
__eq__が作られるため既定ではハッシュ不可- 初期化後の処理は
__post_init__ - 不変にするなら
frozen=True、比較したいならorder=True





