「Pythonでクラスを作るたびに__init__を何度も書くのが面倒くさい…」

そう感じたことはありませんか?実はPython 3.7から、そんな悩みをスッキリ解決してくれるデータクラス(dataclass)という機能が標準搭載されています。

今回は、dataclassの基本的な使い方から、通常クラスとの違い、frozen__post_init__などの応用機能まで、一気に解説していきます!Python中級を目指しているあなたにとって、かなり実践的な内容になっていますよ😊

対象読者と前提知識

Python programming
Python programming / Photo by Myburgh Roux via Pexels
  • ✅ Pythonのクラス・インスタンスの基礎は知っている
  • __init__メソッドを書いたことがある
  • ✅ Python中級レベルへのステップアップを目指している

「クラスって何?」という方は、まずインスタンスとクラスの基礎を押さえてから読んでみてください。

データクラス(dataclass)って何?

dataclassを一言で表すなら、「主にデータを保持するためのクラスを、簡潔に書ける仕組み」です。

イメージとしては、「設計書のテンプレートが自動で埋まる」感じです。通常は自分で書かなければいけない__init____repr____eq__などのメソッドを、Pythonが自動で生成してくれます。

使い方はシンプルで、@dataclassデコレータをクラスに付けるだけ。まずは通常クラスとの書き比べを見てみましょう。

通常クラスとdataclassの違いを比べてみよう

下記は「商品」を表すクラスを、通常クラスとdataclassの両方で書いた例です。

通常クラスで書いた場合

# 通常クラス
class Product:
    def __init__(self, name: str, price: int, stock: int):
        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)


p = Product("リンゴ", 150, 100)
print(p)  # Product(name='リンゴ', price=150, stock=100)

dataclassで書いた場合

from dataclasses import dataclass

# dataclassで書くとこんなにスッキリ!
@dataclass
class Product:
    name: str
    price: int
    stock: int


p = Product("リンゴ", 150, 100)
print(p)  # Product(name='リンゴ', price=150, stock=100)

ポイントをまとめるとこんな感じです👇

  • __init__を自分で書かなくてOK
  • __repr__(print時の表示)が自動生成される
  • __eq__(等値比較)が自動生成される
  • ✅ 型アノテーションでフィールドを定義する

同じことをするのに、行数が圧倒的に少なくなりますよね。これがdataclassの最大の魅力です😊

デフォルト値と field() の使い方

dataclassでは、フィールドにデフォルト値を設定することもできます。

from dataclasses import dataclass, field

@dataclass
class Product:
    name: str
    price: int
    stock: int = 0            # デフォルト値をシンプルに設定
    tags: list = field(default_factory=list)  # リストのデフォルトはfieldを使う!


# stockを省略して作成
p1 = Product("バナナ", 80)
print(p1)  # Product(name='バナナ', price=80, stock=0, tags=[])

# tagsを指定して作成
p2 = Product("オレンジ", 120, 50, ["フルーツ", "柑橘"])
print(p2)  # Product(name='オレンジ', price=120, stock=50, tags=['フルーツ', '柑橘'])

ここが重要です❗

  • リスト・辞書などのミュータブルなオブジェクトをデフォルト値にしたい場合は、field(default_factory=...)を使う必要があります
  • 直接 tags: list = [] と書くとエラーになります(Pythonがわざと弾いてくれる)
  • これは通常クラスでも「全インスタンスがリストを共有してしまうバグ」が起きやすいため、dataclassが安全に守ってくれている仕組みです

frozen=True で不変なクラスを作る


@dataclass(frozen=True)と指定すると、インスタンス生成後に値を書き換えられないイミュータブルなクラスを作れます。

イメージとしては、「読み取り専用の設定ファイル」です。一度作ったら誰にも変更させたくないデータを表すのに便利です。

from dataclasses import dataclass

@dataclass(frozen=True)
class Config:
    host: str
    port: int
    debug: bool = False


cfg = Config("localhost", 8080)
print(cfg)  # Config(host='localhost', port=8080, debug=False)

# 値を書き換えようとするとエラーになる
try:
    cfg.port = 9090
except Exception as e:
    print(f"エラー: {e}")
    # エラー: cannot assign to field 'port'

# frozenなのでハッシュ化できる(辞書のキーやsetの要素にも使える!)
config_set = {cfg}
print(config_set)  # {Config(host='localhost', port=8080, debug=False)}

frozen=Trueにするとうれしいことがあります👇

  • ✅ 意図しない値の書き換えを防げる
  • __hash__が自動生成されるので、辞書のキーやセットの要素として使える
  • ✅ マルチスレッド環境でも安全に使いやすくなる

__post_init__ で初期化後に処理を追加する

「dataclassで自動生成された__init__に、追加の処理を入れたい」というケースも出てきます。そんな時に使うのが__post_init__です。

__post_init__は、__init__が実行されたあとに自動的に呼び出される特別なメソッドです。

from dataclasses import dataclass

@dataclass
class Product:
    name: str
    price: int
    stock: int = 0

    def __post_init__(self):
        # 初期化後のバリデーション処理
        if self.price < 0:
            raise ValueError(f"価格は0以上でなければなりません。受け取った値: {self.price}")
        if self.stock < 0:
            raise ValueError(f"在庫数は0以上でなければなりません。受け取った値: {self.stock}")
        # 名前の前後の空白を自動トリム
        self.name = self.name.strip()


# 正常なインスタンス
p1 = Product("  メロン  ", 500, 10)
print(p1)  # Product(name='メロン', price=500, stock=10)  ← 空白が消えている!

# バリデーションエラーの確認
try:
    p2 = Product("スイカ", -100)
except ValueError as e:
    print(f"エラー: {e}")
    # エラー: 価格は0以上でなければなりません。受け取った値: -100

__post_init__が活躍するシーンはこんな感じです👇

  • ✅ 入力値のバリデーション(不正な値を弾く)
  • ✅ 入力値の自動変換・正規化
  • ✅ 他のフィールドをもとに計算した値のセット

dataclassの主なオプション一覧

最後に、@dataclassデコレータで指定できる主なオプションを整理しておきます。

オプション デフォルト 説明
init True __init__を自動生成するか
repr True __repr__を自動生成するか
eq True __eq__を自動生成するか
order False 比較演算子(<, >など)を自動生成するか
frozen False イミュータブル(書き換え不可)にするか
slots False __slots__を使ってメモリ効率を上げるか(Python 3.10+)

まとめ


今回はPythonのデータクラス(dataclass)について、通常クラスとの違いから応用機能まで解説しました。

  • @dataclassデコレータで__init____repr____eq__が自動生成される
  • ✅ リストなどのデフォルト値にはfield(default_factory=...)を使う
  • frozen=Trueでイミュータブルなクラスが作れる
  • __post_init__でバリデーションや追加処理を書ける

dataclassは「データを持ち運ぶためのクラス」をシンプルかつ安全に書けるとても便利な機能です。APIのレスポンスデータや設定情報の管理など、実際のプロジェクトでも大活躍しますよ😊

「むずかしそう」と思っていたdataclassも、ここまで読めば「使えそう!」に変わったんじゃないでしょうか。ぜひ自分のコードで試してみてください!

📚 関連商品・おすすめ書籍

スッキリわかるPython入門 第2版 (スッキリわかる入門シリーズ)

もしも

スッキリわかるPython入門 第2版 (スッキリわかる入門シリーズ)

初心者に定番のPython入門書

Amazonで見る

徹底攻略! 電子工作&プログラミング Arduinoで学ぶ電子工作完全ガイド

もしも

徹底攻略! 電子工作&プログラミング Arduinoで学ぶ電子工作完全ガイド

電子工作とプログラミングを同時に学べる

Amazonで見る

実践Claude Code入門―現場で活用するためのAIコーディングの思考法

もしも

実践Claude Code入門―現場で活用するためのAIコーディングの思考法

AIコーディングの現場活用法を学ぶ一冊

Amazonで見る

※本記事にはアフィリエイトリンクが含まれます。

ABOUT ME
やまちゃん
これまで学生と社会人を合わせて5000人以上にプログラミング学習を指導。 ゼロからイチをわかりやすく解説する専門家として活動しており、本業ではArduinoを用いたIoT開発とロボットプログラミングが専門。 Pythonを用いたアプリ開発、ウェブアプリケーションの開発で業務の効率化をサポートしています。