dataclass(データクラス)は、データを入れるためのクラスを簡単に作る仕組みです。通常のクラスで__init__や__repr__を毎回書くのは手間ですが、@dataclassを付けるだけで、これらが自動で生成されます。座標・設定・APIのレスポンスなど、「値をまとめて持つ」用途のクラスが、数行で書けます。
つまずきやすいのは、リストや辞書を初期値にするときです。items: list = []のように直接書くとエラーになり、field(default_factory=list)を使う必要があります。これは関数の可変デフォルト引数と同じ落とし穴への対策です。この記事では、実機のPython 3.12で確認しながら、dataclassを整理します。
@dataclassを付けると、__init__・__repr__・__eq__が自動生成されます。- フィールドは
x: intのように型ヒント付きで書きます。デフォルト値も指定できます。 - リスト・辞書を初期値にするときは
field(default_factory=list)を使います。 - 直接
items: list = []と書くとエラーになります。 @dataclass(frozen=True)で変更できない(イミュータブル)クラスになります。asdict()で辞書に変換できます。
土台のクラス(class)、フィールドに付ける型ヒント、可変デフォルトの罠が共通する関数(def)もあわせて参考になります。
dataclassとは(定型コードを自動生成)
通常のクラスで「値をまとめて持つ」だけのものを作ると、__init__でself.x = xを並べ、__repr__で表示を整え……と同じような定型コードを毎回書くことになります。dataclassは、この定型部分を自動生成してくれるため、フィールドの定義だけに集中できます。
基本:@dataclassとフィールド
使い方は簡単で、クラスに@dataclassを付け、フィールドを型ヒント付きで宣言するだけです。__init__を書かなくても、フィールドを引数に取るコンストラクタが自動で作られます。
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int = 0 # デフォルト値も指定できる
# __init__ を書いていないのに、引数を取れる
p = Point(10, 20)
print(p.x, p.y) # 10 20
# デフォルト値が使われる
print(Point(5)) # Point(x=5, y=0)
実機でも、__init__を書いていないのにPoint(10, 20)でインスタンスが作れ、p.x=10、p.y=20になりました。y: int = 0のようにデフォルト値も指定できます。通常のクラスならdef __init__(self, x, y=0): self.x = x; self.y = yと書くところを、dataclassはフィールドの宣言だけで済ませてくれます。
自動生成される__repr__と__eq__
dataclassは__init__だけでなく、見やすい表示の__repr__と、値で比較する__eq__も自動生成します。これらは通常のクラスでは自分で書く必要があるものです。
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int = 0
# __repr__: 中身が分かりやすく表示される
print(Point(10, 20)) # Point(x=10, y=20)
# __eq__: 中身が同じなら等しいと判定される
print(Point(1, 2) == Point(1, 2)) # True
print(Point(1, 2) == Point(1, 3)) # False
実機で確認したところ、print(Point(10, 20))はPoint(x=10, y=20)と中身が分かりやすく表示され、Point(1, 2) == Point(1, 2)はTrueになりました。通常のクラスでは、printすると<__main__.Point object at 0x...>のような分かりにくい表示になり、==もデフォルトでは「同じオブジェクトか」しか見ません(中身が同じでもFalse)。dataclassは、中身を見やすく表示する__repr__と、フィールドの値で等しさを判定する__eq__を自動で用意してくれるため、デバッグや比較がそのまま行えます。これだけでも、データを持つクラスをdataclassにする価値があります。
可変デフォルトはdefault_factory
注意が必要なのが、リストや辞書を初期値にする場合です。items: list = []のように直接書くと、エラーになります。これを避けるため、field(default_factory=...)を使います。
from dataclasses import dataclass, field
# NG: リストを直接デフォルトにするとエラー
# @dataclass
# class Bad:
# items: list = [] # ValueError: mutable default ...
# OK: field(default_factory=list) を使う
@dataclass
class Cart:
items: list = field(default_factory=list)
a = Cart()
b = Cart()
a.items.append("りんご")
print(a.items) # ['りんご']
print(b.items) # [](a と b で別のリスト)
実機で確認したところ、items: list = []と直接書いたdataclassを定義しようとすると、ValueError: mutable default <class 'list'> for field itemsというエラーで定義そのものが失敗しました。これは、もし許してしまうとすべてのインスタンスが同じ1つのリストを共有してしまうためで、Pythonがあえてエラーにして防いでいます(関数の可変デフォルト引数やクラス属性と同じ落とし穴です)。正しくはfield(default_factory=list)を使います。実機でも、これによりaとbがそれぞれ別のリストを持ち、aに追加してもbは空のままでした。リスト・辞書・集合などの初期値は、必ずfield(default_factory=...)で指定してください。default_factoryには、初期値を作る関数(list・dictなど)を渡します。
frozen=Trueでイミュータブル
@dataclass(frozen=True)とすると、作成後に値を変更できない(イミュータブル)クラスになります。誤って書き換えられたくない設定値などに使います。
from dataclasses import dataclass
@dataclass(frozen=True)
class Config:
host: str
port: int
c = Config("localhost", 8080)
print(c.host) # localhost
# 値を変更しようとするとエラー
# c.port = 9000 # FrozenInstanceError
実機でも、frozen=Trueのデータクラスで作ったインスタンスの値を変更しようとすると、FrozenInstanceErrorになりました。frozen=Trueは「作ったら変更しない」データに向いていて、うっかり書き換えるミスを防げます。また、変更できないオブジェクトは辞書のキーや集合の要素にも使える(ハッシュ可能になる)という利点もあります。設定値・座標・定数的なデータなど、変わってほしくない値をまとめるときに便利です。
asdict・フィールドの順番
asdict()を使うと、データクラスを辞書に変換できます。JSONにしたいときなどに便利です。また、フィールドの順番には「デフォルト値なし」を「デフォルト値あり」より前に書くというルールがあります。
from dataclasses import dataclass, asdict
@dataclass
class Point:
x: int
y: int = 0
# 辞書に変換(JSON化などに便利)
print(asdict(Point(1, 2))) # {'x': 1, 'y': 2}
# NG: デフォルトありの後にデフォルトなしは書けない
# @dataclass
# class Bad:
# x: int = 0
# y: int # TypeError: 非デフォルトがデフォルトの後
実機でも、asdict(Point(1, 2))は{'x': 1, 'y': 2}という辞書を返しました。JSONとして保存・送信したいときなどに役立ちます。また、フィールドの順番には注意が必要で、デフォルト値のあるフィールドより後に、デフォルト値のないフィールドを置くことはできません(通常の関数の引数と同じルールです)。デフォルトなしのフィールドを先に、デフォルトありを後に書きます。
主なポイントまとめ
dataclassの要点をまとめます。
| 項目 | ポイント |
|---|---|
| 定義 | @dataclass+フィールドを型ヒント付きで |
| 自動生成 | __init__・__repr__・__eq__ |
| デフォルト値 | y: int = 0 |
| 可変デフォルト | field(default_factory=list)(必須) |
| 変更不可 | @dataclass(frozen=True) |
| 辞書化 | asdict(インスタンス) |
よくある失敗
リストを直接デフォルトにする
items: list = []はエラーです。field(default_factory=list)を使います。
__init__を自分で書いてしまう
dataclassが自動生成します。フィールドを宣言するだけで十分です。
デフォルトありの後にデフォルトなし
順番のルール違反でエラーです。デフォルトなしを先に書きます。
frozenなのに値を変更する
frozen=Trueは変更不可です。変更したいならfrozenを外します。
importを忘れる
from dataclasses import dataclass, fieldのように読み込みます。
よくある質問
@dataclassを付けると、__init__・__repr__・__eq__が自動で生成されます。通常のクラスではこれらを自分で書く必要がありますが、dataclassではフィールドを型ヒント付きで宣言するだけで済みます。値をまとめて持つ用途のクラスを、短く書けます。items: list = []のように直接書くと、すべてのインスタンスでリストを共有してしまうため、Pythonがエラーにします。field(default_factory=list)を使ってください。これにより、各インスタンスが別々のリストを持つようになります。from dataclasses import asdictとしてasdict(インスタンス)を使います。フィールド名をキー、値を値とした辞書が得られ、JSONとして保存・送信したいときなどに便利です。@dataclass(frozen=True)とします。作成後に値を変更しようとするとFrozenInstanceErrorになります。設定値や定数的なデータなど、変わってほしくない値をまとめるのに向いています。辞書のキーや集合の要素にも使えるようになります。まとめ
@dataclassで__init__・__repr__・__eq__が自動生成されます。- フィールドは型ヒント付きで宣言。デフォルト値も指定できます。
- リスト・辞書の初期値は
field(default_factory=list)(直接書くとエラー)。 @dataclass(frozen=True)で変更不可のクラスになります。asdict()で辞書に変換でき、JSON化などに便利です。
dataclassは、データを持つクラスの定型コードを大幅に減らせる、モダンなPythonの便利な機能です。「可変デフォルトはfield(default_factory)」という1点さえ押さえれば、設定・座標・レコードなど、さまざまなデータを安全・簡潔に表現できます。通常のクラスで__init__を書いていた場面は、ぜひdataclassに置き換えてみてください。

