9.

Python タプル型完全ガイド

編集
この記事の要点
  • タプル (tuple) は Python の不変 (immutable) なシーケンス型。(1, 2, 3)
  • 不変なのでハッシュ可能: dict のキー / set の要素として使える (リストは不可)
  • a, b = (1, 2) でアンパック、*rest で残りをまとめてキャプチャ
  • 名前付きフィールドが欲しいときは collections.namedtuple or typing.NamedTuple
  • 型注釈: Python 3.9+ は tuple[int, str]、それ以前は typing.Tuple[int, str]

タプルとは

Python の タプル (tuple) は、複数の値を順序付きで格納する不変 (immutable) のシーケンス型です。リスト (list) と似ていますが、作成後に要素を変更できない点が決定的に異なります。

タプルの作り方

# 基本: 括弧で囲んでカンマ区切り
t = (1, 2, 3)
t = (1, 'hello', 3.14, True)         # 異なる型も混在可

# 括弧は省略可 (カンマがあればタプル)
t = 1, 2, 3
print(type(t))                        # <class 'tuple'>

# 空タプル
empty = ()
empty = tuple()

# ★ 注意: 要素 1 個のタプルは末尾カンマ必須
single = (1,)                         # タプル
not_tuple = (1)                       # ただの int

# イテラブルから生成
t = tuple([1, 2, 3])                  # (1, 2, 3)
t = tuple('hello')                    # ('h', 'e', 'l', 'l', 'o')
t = tuple(range(5))                   # (0, 1, 2, 3, 4)

不変性 (Immutability)

t = (1, 2, 3)

# ❌ 要素の変更は不可
t[0] = 99
# → TypeError: 'tuple' object does not support item assignment

# ❌ append / pop も無い
t.append(4)
# → AttributeError

# ✅ 新しいタプルを作る
t2 = t + (4,)         # (1, 2, 3, 4)
t3 = (0,) + t          # (0, 1, 2, 3)
t4 = t * 3             # (1, 2, 3, 1, 2, 3, 1, 2, 3)

# 注意: タプルが「含む」オブジェクトが mutable なら中身は変えられる
t = ([1, 2], [3, 4])
t[0].append(99)        # OK ((リストは mutable))
print(t)               # ([1, 2, 99], [3, 4])

ハッシュ可能 (Hashable) という強力な性質

タプルが不変であることの最大の効用は、ハッシュ可能であることです。これにより dict のキーや set の要素として使えます (リストは不可)。

# 座標を dict のキーに使う
grid = {}
grid[(0, 0)] = 'start'
grid[(10, 5)] = 'enemy'
print(grid[(0, 0)])                  # 'start'

# 複合キーで集計
sales = {}
sales[('Tokyo', '2025-01')] = 1000
sales[('Tokyo', '2025-02')] = 1200
sales[('Osaka', '2025-01')] = 800

# set の要素
visited = set()
visited.add((3, 4))
visited.add((3, 4))                  # 重複は無視
print(len(visited))                  # 1

# ❌ リストはキーにできない
{[1, 2]: 'value'}
# → TypeError: unhashable type: 'list'

アンパック (Unpacking)

# 基本のアンパック
t = (1, 2, 3)
a, b, c = t
print(a, b, c)                        # 1 2 3

# 関数の複数戻り値 (実はタプル)
def divmod_fn(a, b):
    return a // b, a % b              # タプルとして返している

q, r = divmod_fn(10, 3)
print(q, r)                           # 3 1

# * で残りをキャプチャ (PEP 3132)
first, *rest = (1, 2, 3, 4, 5)
print(first)                          # 1
print(rest)                           # [2, 3, 4, 5] ← list

*init, last = (1, 2, 3, 4, 5)
print(init)                           # [1, 2, 3, 4]
print(last)                           # 5

first, *middle, last = (1, 2, 3, 4, 5)
print(first, middle, last)            # 1 [2, 3, 4] 5

# 値のスワップ (右辺がタプルとして評価される)
a, b = 1, 2
a, b = b, a
print(a, b)                           # 2 1

# 関数呼び出しでアンパック
def add(x, y, z):
    return x + y + z

args = (1, 2, 3)
print(add(*args))                     # 6

namedtuple (名前付きタプル)

位置だけでなくフィールド名でアクセスできるタプル。クラスより軽量で、データ構造表現に最適。

from collections import namedtuple

# 定義
Point = namedtuple('Point', ['x', 'y'])
# または文字列でも可: Point = namedtuple('Point', 'x y')

# 生成
p = Point(10, 20)
print(p.x, p.y)                       # 10 20
print(p[0], p[1])                     # 10 20 (位置でもアクセス可)

# アンパックも可
x, y = p
print(x, y)                           # 10 20

# 不変性は維持
p.x = 99
# → AttributeError: can't set attribute

# 一部だけ書き換えた新インスタンス
p2 = p._replace(x=99)                 # Point(x=99, y=20)

# 辞書化
print(p._asdict())                    # {'x': 10, 'y': 20}

# 全フィールド名
print(Point._fields)                  # ('x', 'y')

typing.NamedTuple (型注釈付き)

from typing import NamedTuple

class Point(NamedTuple):
    x: int
    y: int
    label: str = 'origin'             # デフォルト値も可

p = Point(10, 20)
print(p)                              # Point(x=10, y=20, label='origin')

p2 = Point(x=1, y=2, label='A')

# メソッド定義もできる
class Circle(NamedTuple):
    center: Point
    radius: float

    def area(self) -> float:
        return 3.14159 * self.radius ** 2

c = Circle(Point(0, 0), 5.0)
print(c.area())                       # 78.5398

型注釈

# Python 3.9+ (PEP 585)
def process(pair: tuple[int, str]) -> tuple[int, int]:
    return pair[0], len(pair[1])

# Python 3.8 以前
from typing import Tuple
def process(pair: Tuple[int, str]) -> Tuple[int, int]:
    return pair[0], len(pair[1])

# 可変長
coords: tuple[int, ...] = (1, 2, 3, 4, 5)   # int の任意個

# 固定の組み合わせ
rgb: tuple[int, int, int] = (255, 128, 0)

タプル vs リストの使い分け

用途タプルリスト
変更しない固定データ★ 推奨×
異種混在の構造体的データ (座標 / レコード)★ 推奨不可
同種要素のコレクション (動的に追加)×★ 推奨
dict のキー / set の要素★ 必須不可 (unhashable)
関数の複数戻り値★ 慣例×
メモリ効率やや小 (固定サイズ)余裕領域あり

メモリ効率の比較

import sys

t = (1, 2, 3, 4, 5)
l = [1, 2, 3, 4, 5]

print(sys.getsizeof(t))               # 80 bytes
print(sys.getsizeof(l))               # 104 bytes

# タプルは大量生成しても効率的
# 列挙型代わりに、ループ内で生成しても OK

よくあるパターン

# enumerate: (index, value) のタプルを返す
for i, name in enumerate(['Alice', 'Bob']):
    print(f'{i}: {name}')

# zip: 複数イテラブルを (a, b, c) のタプルに
for a, b in zip([1, 2, 3], ['a', 'b', 'c']):
    print(a, b)

# 辞書のキー・値のペア
d = {'a': 1, 'b': 2}
for k, v in d.items():                # items() は (key, value) のタプル
    print(k, v)

# pandas / numpy のシェイプ
import numpy as np
arr = np.zeros((3, 4, 5))             # shape は tuple
print(arr.shape)                       # (3, 4, 5)

FAQ

Q: タプルとリストはどちらを選ぶべき?
A: 「変更しない固定のデータ」「異なる型のフィールドを持つレコード」はタプル、「動的に増減する同種コレクション」はリスト。複数戻り値や dict キーは必然的にタプル。

Q: x = (1) がタプルにならない
A: 括弧は単なるグルーピング演算子。要素 1 個のタプルは (1,) と末尾カンマが必要です。空タプルだけは () でも OK。

Q: namedtuple と dataclass のどちらを使う?
A: イミュータブルで軽量な構造体は namedtuple、ミュータブルでメソッドも持つクラスは dataclass。Python 3.7+ で @dataclass(frozen=True) で不変 dataclass も作れる。

編集
Post Share
子ページ
  1. タプルの作成
  2. 要素の参照
同階層のページ
  1. 基本的なルール
  2. 変数
  3. 演算子
  4. 標準ライブラリ
  5. 外部ライブラリ
  6. 制御構文
  7. リスト(配列)
  8. タプル
  9. セット
  10. 辞書(dict)
  11. クラスとメソッド
  12. 継承の概念と必要性
  13. 継承の構文
  14. コンストラクタ
  15. cookieの値の設定と取得
  16. 例外処理
  17. 例外を文字列で出力する方法
  18. httpリクエスト(curl)をする方法
  19. Responseオブジェクトの中身の確認
  20. 変数が空かどうか判定する方法
  21. タイムゾーンの設定と現在日時の取得と文字列化
  22. シングルクォーテーションとダブルクォーテーションの違い

最近更新/作成されたページ