【便利】Pythonのdataclassを書いてみよう|実例付

※本サイトにはプロモーション・広告が含まれています。

(最終更新日:2023年8月)

✔こんな方におすすめの記事です

「PythonのDataclassって何だろう?」
「PythonのDataclassを使いこなしたい」
「Dataclassの実践的な使い方を学びたい」

✔当記事で伝える内容は以下の通りです

  • PythonのDataclass概要
  • Dataclassの基本的な書き方と応用
  • Dataclassの具体的な例

当記事では、Python Dataclassの基本的な概念から活用方法まで、実例を交えて詳細に解説しています。

ぜひ最後までご覧ください。

筆者プロフィール

筆者プロフィールアイコン

【現職】プロダクトマネージャー

【副業】ブログ(月間17万PV)/YouTube/Web・アプリ制作

「プログラミング × ライティング × 営業」の経験を活かし、30後半からのIT系職へシフト。当サイトでは、実際に手を動かせるWebアプリの開発を通じて、プログラミングはもちろん、IT職に必要な情報を提供していきます。

【当ブログで紹介しているサイト】

当サイトチュートリアルで作成したデモ版日報アプリ

Django × Reactで開発したツール系Webアプリ

✔人に見せても恥ずかしくないコードを書こう

「リーダブルコード」は、わかりやすく良いコードの定義を教えてくれる本です。

  • 見るからにきれいなコードの書き方
  • コードの分割方法
  • 変数や関数の命名規則

エンジニアのスタンダートとすべき基準を一から解説しています。

何回も読むのに値する本なので、ぜひ手にとって読んでみてください。

はじめに

こちらでは、Pythonのclassdataclassについてお伝えしていきます。

  • Pythonのclass概念
  • dataclassの導入と目的

Pythonのclass概念

Pythonのクラスは、オブジェクト指向プログラミングの中心的な要素であり、関連する変数とメソッドをまとめるための仕組みです。

クラスはオブジェクトの設計図のようなもので、この設計図に基づいてインスタンスが生成されます。

クラスを使うことで、コードの再利用性が向上し、構造が整理され、保守性が高まります。

dataclassの導入と目的

Python 3.7から、標準ライブラリにdataclassが導入されました。

dataclassは、クラスの定義を簡潔にし、データの保持に特化したクラスを作成するためのデコレータです。

主に、属性を持つだけのシンプルなクラスの作成が目的であり、面倒な特殊メソッドの定義を省略できます。

Python dataclassの基本: デコレータの使い方

こちらでは、Pythonのdataclassの基本的な使い方についてお伝えします。

  • @dataclassデコレータの利用
  • 初期化メソッド自動生成
  • デフォルト値指定

@dataclassデコレータの利用

dataclassを使用するには、クラス定義の直前に@dataclassデコレータを付けます。

これにより、クラスはデータクラスとして振る舞い、いくつかの便利な機能が自動的に提供されます。

from dataclasses import dataclass

@dataclass
class Point:
    x: float
    y: float

初期化メソッド自動生成

dataclassでは、__init__メソッドが自動的に生成されます。

これにより、クラスのインスタンスを作成する際に、各属性に値を渡せます。

p = Point(x=1.0, y=2.0)
print(p)  # Point(x=1.0, y=2.0)

デフォルト値指定

dataclassのフィールドには、デフォルト値を指定できます。

インスタンスの作成時に、特定の属性に値を渡さなくても、デフォルト値が使用されます。

@dataclass
class Rectangle:
    width: float
    height: float = 10.0

rect = Rectangle(width=5.0)
print(rect)  # Rectangle(width=5.0, height=10.0)

dataclassの便利な機能

こちらでは、dataclassが提供する便利な機能についてお伝えします。

  • 自動生成されるメソッドの解説
  • frozen属性: イミュータブルなデータクラス
  • order属性: 順序付けを行う
  • デフォルトファクトリ関数
  • 可変なデフォルト値

自動生成されるメソッドの解説

dataclassを作成すると自動で生成されるメソッドがあります。

詳しく見ていきましょう。

  • repr
  • eq

repr

dataclassでは、__repr__メソッドが自動生成されます。

このメソッドは、クラスのインスタンスを文字列形式で表現する役割を持ちます。

@dataclass
class Circle:
    radius: float

c = Circle(radius=5.0)
print(c)  # Circle(radius=5.0)

このメソッドにより、クラスのインスタンスを文字列形式で見やすく表現できます。

デバッグや表示の際に便利な機能となります。

eq

dataclassでは、__eq__メソッドも自動生成されます。

これにより、2つのインスタンスの属性が等しいかどうかを簡単に比較できるのです。

c1 = Circle(radius=5.0)
c2 = Circle(radius=5.0)
print(c1 == c2)  # True

frozen属性: イミュータブルなデータクラス

dataclassにはfrozen属性があり、これをTrueに設定すると、データクラスのインスタンスがイミュータブルになります。

これは、一度作成されたインスタンスの属性を変更できなくするものです。

@dataclass(frozen=True)
class ImmutablePoint:
    x: float
    y: float

order属性: 順序付けを行う

order属性をTrueに設定すると、インスタンス間での比較演算が可能になります。

これは、<<=>>=などの比較演算子をサポートするものです。

from dataclasses import dataclass

@dataclass(order=True)
class Person:
    name: str
    age: int
    city: str

# Personクラスのインスタンスを作成
person1 = Person(name="Alice", age=30, city="New York")
person2 = Person(name="Bob", age=25, city="Los Angeles")

# 比較演算が可能
print(f"person1 > person2: {person1 > person2}")
print(f"person1 < person2: {person1 < person2}")
print(f"person1 >= person2: {person1 >= person2}")
print(f"person1 <= person2: {person1 <= person2}")

"""出力結果
person1 > person2: True
person1 < person2: False
person1 >= person2: True
person1 <= person2: False
"""

@dataclass(order=True)とすることで、Personクラスのインスタンス間での比較演算が可能です。

こちらのPersonクラスでは、name属性を基準に比較されます。

また同じ値の場合は、次の属性で比較がおこなわれます(age, cityの順)。

これにより、インスタンスを比較する際に便利な機能を持つクラスを簡単に作成できます。

デフォルトファクトリ関数

field関数を使用して、デフォルト値をファクトリ関数で生成できます。

これは、特に可変オブジェクト(リストや辞書など)をデフォルト値として使用する際に有用です。

from dataclasses import dataclass, field

@dataclass
class Inventory:
    items: list = field(default_factory=list)

inventory = Inventory()
inventory.items.append("apple")
print(inventory)  # Inventory(items=['apple'])

可変なデフォルト値

デフォルトファクトリを使用する代わりに、直接可変なデフォルト値を設定することもできます。

ただしこれはすべてのインスタンスで共有されるため注意が必要です。

初期化後の処理とカスタマイズ

こちらでは、dataclassの初期化後の処理とカスタマイズについてお伝えします。

  • initメソッドのカスタマイズ
  • クラス変数と初期化限定変数
  • キーワード専用パラメータの再整理

initメソッドのカスタマイズ

dataclassでは、__post_init__メソッドを使用して、初期化後に追加の処理をおこなえます。

@dataclass
class Circle:
    radius: float

    def __post_init__(self):
        self.area = 3.14 * (self.radius ** 2)

circle = Circle(2)
print(circle.area)  # 12.56

クラス変数と初期化限定変数

dataclassでは、クラス変数や初期化時に限定された変数を使えます。

これにより、データクラスがさらに柔軟になります。

from dataclasses import dataclass, field

@dataclass
class Book:
    title: str
    author: str
    year: int
    genre: str = field(default="Unknown")
    copies_available: int = field(init=False, default=0)

    # クラス変数
    total_copies: int = 0

    def __post_init__(self):
        # 初期化時に処理される変数
        self.copies_available = self.total_copies

# クラス変数の利用
Book.total_copies = 1000

# Bookクラスのインスタンスを作成
book1 = Book(title="Book A", author="Author X", year=2022)
book2 = Book(title="Book B", author="Author Y", year=2020, genre="Fiction")

# データの表示
print(book1)
print(book2)

"""出力結果
Book(title='Book A', author='Author X', year=2022, genre='Unknown', copies_available=1000)
Book(title='Book B', author='Author Y', year=2020, genre='Fiction', copies_available=1000)
"""

キーワード専用パラメータの再整理

dataclassで、パラメータをキーワード専用にすることも可能です。

これにより、パラメータの順序に依存せずに、引数を渡せます。

from dataclasses import dataclass

@dataclass
class Point:
    x: int
    y: int
    z: int = 0

# キーワード専用でパラメータを渡す
point1 = Point(x=1, y=2, z=3)
point2 = Point(z=5, y=4, x=6)

# データの表示
print(point1)
print(point2)

"""出力結果
Point(x=1, y=2, z=3)
Point(x=6, y=4, z=5)
"""

dataclassの応用: 継承とDescriptor-typedfields

こちらでは、dataclassの継承とDescriptor-typedfieldsの活用についてお伝えします。

  • dataclassの継承
  • Descriptor-typedfieldsの活用

dataclassの継承

dataclassは他のdataclassから継承できます。

これにより、既存のdataclassを拡張して新しいdataclassの作成が可能です。

from dataclasses import dataclass

@dataclass
class Person:
    name: str
    age: int

@dataclass
class Employee(Person):
    position: str
    salary: float

# Personクラスのインスタンスを作成
person = Person(name="Alice", age=30)

# Employeeクラスのインスタンスを作成
employee = Employee(name="Bob", age=25, position="Manager", salary=5000.0)

# データの表示
print(person)
print(employee)

"""出力結果
Person(name='Alice', age=30)
Employee(name='Bob', age=25, position='Manager', salary=5000.0)
"""

Descriptor-typedfieldsの活用

dataclassでは、ディススクリプタとして型ヒントを使用できます。

これにより、属性の取得や設定に関するカスタムロジックを実装できるのです。

from dataclasses import dataclass
from typing import List

class PositiveNumber:
    def __init__(self, value):
        if value <= 0:
            raise ValueError("Value must be a positive number.")
        self._value = value

    def __get__(self, instance, owner):
        return self._value

    def __set__(self, instance, value):
        if value <= 0:
            raise ValueError("Value must be a positive number.")
        self._value = value

@dataclass
class Product:
    name: str
    price: PositiveNumber
    tags: List[str]

# Productクラスのインスタンスを作成
product1 = Product(name="Product A", price=1000, tags=["tag1", "tag2"])
product2 = Product(name="Product B", price=2000, tags=["tag3"])

# データの表示
print(product1)
print(product2)

# ディスクリプタを使用してprice属性にアクセス
print(product1.price)  # 1000
print(product2.price)  # 2000

# ディスクリプタを使用してprice属性を変更
product1.price = 1500
print(product1.price)  # 1500

# ディスクリプタを使用して無効な値を設定しようとすると例外が発生
try:
    product2.price = -500  # ValueError: Value must be a positive number.
except ValueError as e:
    print(e)

"""出力結果
Product(name='Product A', price=1000, tags=['tag1', 'tag2'])
Product(name='Product B', price=2000, tags=['tag3'])
1000
2000
1500
Value must be a positive number.
"""

dataclassの実践例と使いどころ

こちらでは、dataclassの実践例と、それが活用されるべきシーンについてお伝えします。

  • 実践例: 簡単なTo-doリストアプリ
  • dataclassの適切な活用シーン

実践例: 簡単なTo-doリストアプリ

dataclassを使って簡単なTo-doリストアプリを作ってみましょう。

各タスクを表すクラスと、タスクのリストを管理するクラスを定義します。

from dataclasses import dataclass, field

@dataclass
class Task:
    title: str
    completed: bool = False

@dataclass
class TodoList:
    tasks: list = field(default_factory=list)

    def add_task(self, task: Task):
        self.tasks.append(task)

todo_list = TodoList()
todo_list.add_task(Task(title="Buy milk"))
todo_list.add_task(Task(title="Read book", completed=True))

print(todo_list)

dataclassの適切な活用シーン

dataclassは、属性を持つデータを表現するシンプルなクラスを定義する場合に適しています。

特に、データベースの行やJSONオブジェクトなど、データ構造に注目が集まる場合に役立つものです。

しかし複雑な振る舞いや状態管理が必要な場合、通常のクラスの方が適しているかもしれません。

他のライブラリとの比較

こちらでは、dataclassと他のライブラリとの違いについてお伝えします。

  • attrsライブラリとの違い
  • namedtupleとの違い

attrsライブラリとの違い

attrsライブラリも、dataclassと同様に、クラスの定義を簡略化するためのものです。

attrsdataclassよりも前から存在し、より多くの機能を持っています。

ただしdataclassは標準ライブラリに含まれているため、外部依存を減らしたい場合にはdataclassが適しています。

namedtupleとの違い

namedtupleはイミュータブルなデータコンテナを提供します。

dataclassと比較すると、namedtupleは属性を変更できないのが特徴。

dataclassではfrozen属性を使ってイミュータブルなクラスを作成することもできますが、dataclassはデフォルトで可変です。

また、namedtupleはタプルのサブクラスであり、インデックスでアクセスすることも可能ですが、dataclassは通常のクラスのように動作します。

まとめ

dataclassは、Pythonでデータを持つクラスをシンプルに定義するためのデコレータです。

属性の自動初期化、比較メソッドの自動生成、イミュータブルオプションなど、便利な機能が多く含まれています。

dataclassを使用することで、コードの冗長性を減らし、読みやすく保守しやすいコードを書けるのが大きなメリットです。

ただし複雑なロジックや状態管理が必要な場合は、通常のクラス定義を検討しましょう。

dataclassはツールのひとつであり、適切な場面で効果的に使用することが重要です。

タイトルとURLをコピーしました