1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[Python] データ構造、どれ使う?

Last updated at Posted at 2025-06-01

Pythonでのデータ構造の保持方法について

Pythonにおいてデータを保持するクラスはいくつかありますが、その使い分けについて備忘録がてら記事を作成します。この記事では、Pythonの代表的なデータ構造である tuple, dict, collections.namedtuple, types.SimpleNamespace, typing.NamedTuple, dataclasses.dataclass の特徴と、それぞれの使い分けについてまとめています。

1. Pythonの主要なデータ構造とそれぞれの特徴について

tuple

不変なデータ構造の基本形

タプルは、複数の要素を順序付けて格納できるイミュータブル(不変)なデータ型です。一度作成すると、その内容を変更することはできません。

  • 特徴:
    • イミュータブル(変更不可)
    • 要素へのアクセスはインデックス (my_tuple[0])
    • 軽量で高速
    • 辞書のキーとして使用可能
  • 定義例:
    point = (10, 20)
    colors = ('red', 'green', 'blue')
    
  • 主な用途:
    • 変更されたくない固定的な値の組(座標、RGB値など)
    • 関数の返り値として複数の値を返す場合
  • メリット: シンプル、メモリ効率が良い、不変性が保証される
  • デメリット: インデックスでのアクセスは、要素数が増えると可読性が低下する

dict

キーと値のマッピング

辞書は、キーと値のペアを格納するミュータブル(可変)なデータ型です。キーを使って効率的に値にアクセスできます。

  • 特徴:
    • ミュータブル(変更可能)
    • キーを使って値にアクセス (my_dict['key'])
    • キーは一意でハッシュ可能である必要がある
    • Python 3.7以降では挿入順序が保持される1
  • 定義例:
    person = {'name': 'Alice', 'age': 30}
    config = dict(host='localhost', port=8080)
    
  • 主な用途:
    • JSONのような構造化データの表現
    • 設定情報やパラメータの管理
    • オブジェクトの属性を動的に格納
  • メリット: 柔軟性が高い、キーによる直感的なアクセス
  • デメリット: タプルに比べてメモリ消費がやや大きい傾向、属性アクセス (obj.key) はできない

collections.namedtuple

名前でアクセスできるタプル

namedtuple は、Python の collections モジュールに含まれるファクトリ関数で、タプルのサブクラスとして各要素に名前(フィールド名)を付けてアクセスできるようにしたものです。タプルの軽量さと不変性を保ちつつ、可読性を向上させます。

  • 特徴:
    • イミュータブル
    • インデックスと属性名の両方でアクセス可能 (point[0], point.x)
    • タプルベースなのでメモリ効率が良い
    • _asdict(), _replace() などのヘルパーメソッドを持つ
  • 定義例:
    from collections import namedtuple
    Point = namedtuple('Point', ['x', 'y'])
    p = Point(10, 20)
    
  • 主な用途:
    • CSVの行データやデータベースのレコードのような、構造が固定されたデータの表現
    • 関数の複数の返り値を分かりやすくまとめたい場合
  • メリット: 可読性が向上、タプルの利点を継承、軽量、Pythonの標準モジュールに入っている
  • デメリット: 型ヒントの直接的なサポートはない
  • 追記:
    • namedtupleのファクトリ関数は第二引数にstrをとった場合、カンマをスペースに置換した上でスペースを区切り文字としてsplit()を呼び出す、すなわち上の例は
      Point = namedtuple('Point', 'x y')
      # もしくは
      Point = namedtuple('Point', 'x,y')
      
      と書いてもよい
    • rename=True オプションを指定すると、適切でないフィールド名(予約語や名前の重複など)は自動的に位置を示す名前に置き換えられる2
      例えば Point = namedtuple('Point', ['abc', 'def', 'ghi', 'abc'], rename=True) とした場合、フィールド名は ('abc', '_1', 'ghi', '_3') になる
      rename=False (デフォルト) の場合は ValueError が発生する
    • 型を付けたい場合は後述のtyping.NamedTupleを使うとよい
    • Python3.7以降ではデフォルト値が設定できるようになった
      Point = namedtuple('Point', ['x', 'y', 'z'], defaults=[0, 1])
      
      と書くことでyのデフォルト値が0に、zのデフォルト値が1に設定される

types.SimpleNamespace

手軽な属性アクセスオブジェクト

SimpleNamespacetypes モジュールにあるシンプルなオブジェクトで、属性の追加・アクセスがドット表記で可能です。インスタンス化時にキーワード引数で属性を初期化できます。

  • 特徴:
    • ミュータブル
    • 属性名でアクセス (config.host)
    • インスタンス化後も属性の追加・変更・削除が可能
    • __repr__ など基本的な特殊メソッドは提供される
  • 定義例:
    from types import SimpleNamespace
    config = SimpleNamespace(host='localhost', port=8080, debug=True)
    print(config.host)  # 出力: localhost
    config.user = 'admin' # 後から属性を追加可能
    print(config.user)  # 出力: admin
    
  • 主な用途:
    • 少数の属性をまとめて手軽に扱いたい場合
    • dict のキーアクセスよりも属性アクセスを好む場合
    • 設定値のグループ化や、一時的なデータの入れ物として
  • メリット: 非常に手軽、属性アクセスが直感的
  • デメリット: 型ヒントの直接的なサポートはない、不変性がない、namedtupledataclassほどの機能はない

typing.NamedTuple

型ヒント付きのnamedtuple

typing.NamedTuplecollections.namedtuple と似ていますが、クラス構文を使い、各フィールドに型ヒントを付与できる点が大きな特徴です。

  • 特徴:
    • イミュータブル
    • インデックスと属性名の両方でアクセス可能
    • フィールドごとに型ヒントを明記
    • デフォルト値を指定可能 (Python 3.6.1以降)3
    • クラス構文で定義
  • 定義例:
    from typing import NamedTuple
    
    class Point(NamedTuple):
        x: int
        y: int = 0 # デフォルト値を指定
    
    p1 = Point(10) # yはデフォルト値の0になる
    p2 = Point(x=5, y=15)
    
  • 主な用途:
    • 型安全性を重視する場面でのnamedtuple
    • APIのデータ構造定義
    • collections.namedtuple の型ヒント強化版として
  • メリット: 型安全、可読性が高い、デフォルト値サポート、静的解析ツールとの親和性
  • デメリット: collections.namedtuple に比べてやや記述量が増える

dataclasses.dataclass

メソッド付きデータ構造

dataclass デコレータは、主にデータを保持するためのクラスを簡単に作成する機能を提供します。__init__, __repr__, __eq__, __hash__ (条件による) などの特殊メソッドを自動生成してくれるため、ボイラープレートコードを大幅に削減できます。

  • 特徴:
    • デフォルトでミュータブル(frozen=True でイミュータブルに変更可能)
    • 属性名でアクセス
    • フィールドごとに型ヒントを推奨(必須ではないが一般的)
    • デフォルト値、初期化後の処理 (__post_init__)、フィールドごとのメタデータなど高機能
    • 比較メソッドなどを自動生成
    • 継承も可能
  • 定義例:
    from dataclasses import dataclass, field
    
    @dataclass(frozen=True) # イミュータブルにする場合
    class InventoryItem:
        name: str
        unit_price: float
        quantity_on_hand: int = 0
    
        def total_cost(self) -> float:
            return self.unit_price * self.quantity_on_hand
    
    item = InventoryItem("Apple", 0.5, 100)
    print(item)
    print(item.total_cost())
    
  • 主な用途:
    • 状態を持つオブジェクトの定義
    • APIのレスポンス/リクエストモデル
    • 設定オブジェクト
    • メソッドを持つ簡単なクラスを素早く作りたい場合
  • メリット: ボイラープレートコードを大幅削減、高機能で柔軟、型ヒントとの親和性が高い、可変/不変を選択可能
  • デメリット: namedtuple 系に比べると、ややオーバーヘッドが大きい場合がある(slots=True (Python 3.10+)で改善可)4

通常の class

すべてを制御できる基本形

属性やメソッドを自由に定義でき、最も柔軟性が高い方法です。

  • 特徴:
    • ミュータビリティは設計次第で可変にも不変にもできる
    • 属性アクセス (obj.field)
    • メソッドを自由に定義可能
    • 継承、カプセル化、ポリモーフィズムなど、オブジェクト指向の全機能を活用可能
    • __init__, __repr__, __eq__ などの特殊メソッドは手動で実装
  • 定義例:
    class Point:
        def __init__(self, x: int, y: int):
            self.x = x
            self.y = y
    
        def __repr__(self) -> str:
            return f"Point(x={self.x}, y={self.y})"
    
        def distance_from_origin(self) -> float:
            return (self.x**2 + self.y**2)**0.5
    
  • 主な用途:
    • データだけでなく、複雑な振る舞い(メソッド)を持つオブジェクトを定義する場合
    • dataclass の自動生成機能では不足する場合や、より細かな制御が必要な場合
    • 既存のライブラリやフレームワークのクラスを継承する場合
  • メリット: 最大限の自由度と制御
  • デメリット: 単純なデータコンテナとしては記述量が多くなる(dataclass はこの点を解消)

2. データ構造の比較と使い分け

ここからは、紹介したデータ構造を比較し、どのような場合にどれを選択すべきかの指針を示します。

特徴比較表

特徴 tuple collections.namedtuple dict types.SimpleNamespace typing.NamedTuple dataclasses.dataclass 通常のclass
不変性 不変 不変 可変 可変 不変 可変 (or 不変) 設計次第
アクセス インデックス インデックス, 属性 キー 属性 インデックス, 属性 属性 属性
フィールド名 なし あり あり (キー) あり あり あり あり
型ヒント 間接的 なし (直接) 間接的 なし (直接) ◎ (必須) ◎ (推奨) 手動
デフォルト値 なし あり (Python 3.7+)2 get() なし あり あり 手動
自動メソッド なし _asdict() なし __repr__ __init__, __repr__ __init__ 手動
手軽さ ×
主な用途 いくつかの値をセットで扱い、その順番や中身が変わらないことを保証したい場面 決まった構造のデータを読みやすく扱いたいが、中身は変えたくない場面 後から値を追加したい、また名前を使って簡単に情報を取り出したい場面 いくつかの属性を手軽にまとめて使いたい場面 namedtupleの便利さに加え、型安全にしたい場面 主にデータを保持するためのクラスを少ない手間で定義し、すぐに使い始めたい場面 データとメソッドを組み合わせて、より複雑なものを作りたい場面

どれ使う?

  • 単純な不変シーケンスで、フィールド名が不要な場合: tuple
    • 例: 関数の引数として固定的な少数の値を渡す、座標 (x, y) を一時的に保持するが名前アクセスは不要
  • 不変で、フィールド名によるアクセスで可読性を上げたい、かつ手軽に定義したい場合 (型ヒントが最優先でない場合): collections.namedtuple
    • 例: CSVの行データ、DBのレコード、関数の複数の返り値をまとめる
  • 可変で、キーと値のペアを柔軟に扱いたい場合、JSONライクなデータ: dict
    • 例: 設定情報、動的にキーが増減するデータ、JSONデータのパース結果
  • ミュータブルで属性アクセスしたい、超手軽なオブジェクトが必要な場合: types.SimpleNamespace
    • 例: ちょっとした設定のグループ化、名前空間として一時的に値をまとめたいとき
  • 不変で、フィールド名と型ヒントが明確に必要な場合、特に静的解析の恩恵を受けたい場合: typing.NamedTuple
    • 例: collections.namedtuple の型ヒント強化版。APIのデータ構造定義など
  • 型ヒントが必須で、デフォルト値や比較メソッドなどの自動生成が欲しい、可変性も制御したい、よりオブジェクト指向的なデータ構造: dataclasses.dataclass
    • 例: 状態を持つオブジェクト、APIのレスポンス/リクエストモデル、設定オブジェクト。typing.NamedTuple より高機能
  • データだけでなく複雑なロジックも持たせたい、最大限の自由度が必要な場合: 通常の class
    • 例: 独自のメソッドや複雑な継承関係を持つオブジェクト

選択のフローチャート:

データをどのように扱いたいか?

├── 単純な値の組で、不変、順序が重要、名前アクセス不要 → tuple

├── 不変で、名前アクセスが欲しい
│ │
│ ├── 型ヒントが必須、デフォルト値も欲しい → typing.NamedTuple
│ │ (より高機能なら dataclass(frozen=True))
│ └── 手軽さ重視、型ヒントは厳密でなくてよい → collections.namedtuple

├── 可変で、キーと値のペア、柔軟性が重要 → dict

├── 可変で、属性アクセスが欲しい、超手軽に → types.SimpleNamespace

└── 型ヒントを活用し、メソッド自動生成、可変/不変を選択したい、よりオブジェクト指向的 → dataclasses.dataclass

└── さらに複雑なロジックや完全な制御が必要 → 通常の class

  1. https://docs.python.org/ja/3.7/reference/datamodel.html#the-standard-type-hierarchy

  2. https://docs.python.org/ja/3/library/collections.html#collections.namedtuple 2

  3. https://docs.python.org/ja/3.6/library/typing.html

  4. https://www.python.jp/news/wnpython310/dataclass-with-slots.html

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?