タプル(tuple)とは何か
タプルの基本概念
タプルは、順序付けられた変更不可能(イミュータブル)なコレクションデータ型だ。複数の要素を一つの変数にまとめて格納できるが、一度作成すると要素を変更、追加、削除することができない。 タプルは以下のように定義される。
# 空のタプル
empty_tuple = ()
# 要素が1つのタプル(カンマが必要)
single_element = (1,) # カンマがないとただの数値になってしまう
wrong_tuple = (1) # これはint型の1になる
# 複数要素のタプル
coordinates = (35.6895, 139.6917) # 東京の座標
user_data = ("田中太郎", 30, "東京都")
# 括弧なしでも定義可能
another_tuple = 1, 2, 3, 4, 5
print(type(coordinates)) # <class 'tuple'>
タプルは丸括弧()を使って定義し、各要素はカンマ「,」で区切る。実は括弧は省略可能で、カンマで区切られた値の並びがタプルの本質だ。
タプルの要素へのアクセス
タプルの要素には、リストと同様に0から始まるインデックスでアクセスできる。
fruits = ("りんご", "バナナ", "オレンジ", "ぶどう", "メロン")
# 正のインデックス(先頭から)
print(fruits[0]) # りんご
print(fruits[2]) # オレンジ
# 負のインデックス(末尾から)
print(fruits[-1]) # メロン
print(fruits[-2]) # ぶどう
# タプルの長さを取得
print(len(fruits)) # 5
タプルの便利な操作
スライシング
タプルはリストと同様にスライシングをサポートしている。 リストと同様に[]を用いてスライシングをする。
numbers = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
# 部分タプルの取得
print(numbers[2:7]) # (2, 3, 4, 5, 6)
print(numbers[:4]) # (0, 1, 2, 3)
print(numbers[5:]) # (5, 6, 7, 8, 9)
# ステップを指定
print(numbers[::2]) # (0, 2, 4, 6, 8) - 2つおき
print(numbers[::-1]) # (9, 8, 7, 6, 5, 4, 3, 2, 1, 0) - 逆順
スライシングで取得した結果も新しいタプルとして返される。元のタプルは変更されない。
パッキングとアンパッキング
タプルの最も強力な機能の一つが、パッキング(複数の値をまとめる)とアンパッキング(複数の変数に分解する)だ。
# タプルパッキング(括弧は省略可能)
coordinates = 35.6895, 139.6917
print(coordinates) # (35.6895, 139.6917)
# タプルアンパッキング
latitude, longitude = coordinates
print(f"緯度: {latitude}") # 緯度: 35.6895
print(f"経度: {longitude}") # 経度: 139.6917
# 複数変数への同時代入
name, age, city = "山田花子", 28, "大阪府"
print(f"{name}さんは{age}歳で{city}在住です")
# 変数の値を入れ替える(タプルアンパッキングの応用)
a = 10
b = 20
a, b = b, a # スワップ
print(f"a={a}, b={b}") # a=20, b=10
この機能により、複数の値を簡潔に扱えるようになる。関数から複数の値を返す際にも頻繁に使われる。
タプルのメソッド
タプルは変更不可能なため、メソッドは限定的だ。主に`count()`と`index()`の2つしかない。
my_tuple = (1, 2, 2, 3, 2, 4, 5)
# count: 指定した値の出現回数をカウント
count_of_2 = my_tuple.count(2)
print(f"2の出現回数: {count_of_2}") # 2の出現回数: 3
# index: 指定した値が最初に現れるインデックスを返す
index_of_3 = my_tuple.index(3)
print(f"3の位置: {index_of_3}") # 3の位置: 3
# 範囲を指定して検索
index_of_second_2 = my_tuple.index(2, 2, 7) # インデックス2〜7の範囲で検索
print(f"2番目の2の位置: {index_of_second_2}") # 2番目の2の位置: 2

【Udemy】Python入門講座(0からPythonの基礎を体系的に学んで、生成AIでバイブコーディング)
受講生3000人ほどの人気講座。Pythonの基礎から生成AI活用までを20時間ほどで一気に学びます。業務で活かせるPythonの基礎知識はしっかりと身につきます。
リストとタプルの5つの重要な違い
1. 変更可能性(最も重要な違い)
リストはミュータブル(変更可能)、タプルはイミュータブル(変更不可能)だ。これが両者の根本的な違いであり、使い分けの基準になる。
# リスト:要素の変更が可能
my_list = [1, 2, 3]
print(id(my_list)) # 例: 140320164963976
my_list[0] = 100 # 要素を変更
my_list.append(4) # 要素を追加
print(my_list) # [100, 2, 3, 4]
print(id(my_list)) # 同じID - オブジェクト自体は変わらない
# タプル:要素の変更は不可能
my_tuple = (1, 2, 3)
print(id(my_tuple)) # 例: 140320165124352
# my_tuple[0] = 100 # エラー!TypeError: 'tuple' object does not support item assignment
# 新しいタプルを作成することは可能
my_tuple = my_tuple + (4,)
print(my_tuple) # (1, 2, 3, 4)
print(id(my_tuple)) # 異なるID - 新しいオブジェクトが作成された
2. メモリ使用量
タプルはサイズが固定のため、作成時に必要なメモリだけを確保して効率的に使用できる。一方、リストは要素数が変わる可能性があるため、余分なメモリ領域を確保しておく必要がある。
import sys
# 同じ要素を持つリストとタプルのメモリ使用量を比較
my_list = [1, 2, 3, 4, 5]
my_tuple = (1, 2, 3, 4, 5)
print(f"リストのメモリサイズ: {sys.getsizeof(my_list)}バイト")
print(f"タプルのメモリサイズ: {sys.getsizeof(my_tuple)}バイト")
# 実行結果例:
# リストのメモリサイズ: 104バイト
# タプルのメモリサイズ: 80バイト
# より大きなデータで比較
large_list = list(range(1000))
large_tuple = tuple(range(1000))
print(f"\n大きなリストのメモリサイズ: {sys.getsizeof(large_list)}バイト")
print(f"大きなタプルのメモリサイズ: {sys.getsizeof(large_tuple)}バイト")
# 実行結果例:
# 大きなリストのメモリサイズ: 8856バイト
# 大きなタプルのメモリサイズ: 8024バイト
この例からわかるように、タプルはリストよりもメモリ効率が良い。
3. 辞書のキーとして使用できるか
イミュータブルなタプルは辞書のキーとして使用できるが、ミュータブルなリストはキーにできない。
# タプルは辞書のキーとして使用可能
locations = {
(35.681236, 139.767125): "東京タワー",
(35.710063, 139.810700): "スカイツリー",
(34.693738, 135.502165): "大阪城"
}
tokyo_tower = locations[(35.681236, 139.767125)]
print(tokyo_tower) # 東京タワー
# リストは辞書のキーとして使用不可
try:
invalid_dict = {
[35.681236, 139.767125]: "東京タワー" # エラー!
}
except TypeError as e:
print(f"エラー: {e}") # unhashable type: 'list'
座標や複合的なIDなど、複数の値を組み合わせたキーが必要な場合にタプルを利用する。

【Udemy】Python入門講座(0からPythonの基礎を体系的に学んで、生成AIでバイブコーディング)
受講生3000人ほどの人気講座。Pythonの基礎から生成AI活用までを20時間ほどで一気に学びます。業務で活かせるPythonの基礎知識はしっかりと身につきます。
実行速度の比較:タプル vs リスト
生成速度の比較
タプルとリストのどちらが速いのか、実際に計測してみよう。
import timeit
# タプル生成の速度(1000000回生成)
tuple_time = timeit.timeit('x = (1, 2, 3, 4, 5)', number=1000000)
print(f"タプル生成時間: {tuple_time:.6f}秒")
# リスト生成の速度(1000000回生成)
list_time = timeit.timeit('x = [1, 2, 3, 4, 5]', number=1000000)
print(f"リスト生成時間: {list_time:.6f}秒")
# 比較
print(f"リストはタプルの{list_time/tuple_time:.2f}倍遅い")
# 実行結果例:
# タプル生成時間: 0.006081秒
# リスト生成時間: 0.034472秒
# リストはタプルの5.67倍遅い
タプルの生成はリストよりも約5〜6倍速い。これは、タプルがイミュータブルなため、将来の変更に備えた余分な処理が不要だからだ。
要素アクセスの速度比較
次に、要素にアクセスする速度を比較してみよう。
import timeit
# 大きなデータで比較
size = 10000
# タプルでの要素アクセス
tuple_access = timeit.timeit(
'x[5000]',
setup=f'x = tuple(range({size}))',
number=1000000
)
print(f"タプル要素アクセス時間: {tuple_access:.6f}秒")
# リストでの要素アクセス
list_access = timeit.timeit(
'x[5000]',
setup=f'x = list(range({size}))',
number=1000000
)
print(f"リスト要素アクセス時間: {list_access:.6f}秒")
# 比較
print(f"速度差: {abs(list_access - tuple_access):.6f}秒")
print(f"タプルの方が{((list_access/tuple_access - 1) * 100):.2f}%速い")
# 実行結果例:
# タプル要素アクセス時間: 0.007751秒
# リスト要素アクセス時間: 0.007443秒
# タプルの方が-3.96%速い
要素アクセスに関しては、タプルの方がわずかに遅いが、差は小さい。
イテレーション(繰り返し処理)の速度比較
for文でのループ処理の速度も比較してみよう。
import timeit
size = 10000
# タプルでのイテレーション
tuple_iter = timeit.timeit(
'for item in x: pass',
setup=f'x = tuple(range({size}))',
number=10000
)
print(f"タプルイテレーション時間: {tuple_iter:.6f}秒")
# リストでのイテレーション
list_iter = timeit.timeit(
'for item in x: pass',
setup=f'x = list(range({size}))',
number=10000
)
print(f"リストイテレーション時間: {list_iter:.6f}秒")
# 比較
print(f"タプルの方が{((list_iter/tuple_iter - 1) * 100):.2f}%速い")
# 実行結果例:
# タプルイテレーション時間: 0.299163秒
# リストイテレーション時間: 0.306977秒
# タプルの方が2.61%速い
イテレーション処理でもタプルの方が2-3%程度速いがほとんど誤差の範囲内だ。

【Udemy】Python入門講座(0からPythonの基礎を体系的に学んで、生成AIでバイブコーディング)
受講生3000人ほどの人気講座。Pythonの基礎から生成AI活用までを20時間ほどで一気に学びます。業務で活かせるPythonの基礎知識はしっかりと身につきます。
タプルとリストの使い分けガイド
タプルを使うべき場面
以下のような場合はタプルが適している。 1. データが変更されないことを保証したい場合
# 設定値や定数
DATABASE_CONFIG = ("localhost", 5432, "mydb", "user", "password")
# 曜日など固定的なデータ
DAYS_OF_WEEK = ("月", "火", "水", "木", "金", "土", "日")
2. 辞書のキーとして使いたい場合
# 座標をキーにした地図データ
map_data = {
(0, 0): "スタート地点",
(10, 5): "チェックポイント1",
(20, 15): "ゴール"
}
3. 関数から複数の値を返す場合
def get_user_info(user_id):
# データベースから取得したと仮定
name = "田中太郎"
age = 30
city = "東京都"
return name, age, city # タプルで返す
# アンパッキングで受け取る
user_name, user_age, user_city = get_user_info(123)
4. メモリ効率や速度を重視する場合
# 大量の座標データを扱う場合
coordinates = [
(35.681, 139.767),
(34.693, 135.502),
(43.064, 141.347),
# ... 数千〜数万件のデータ
]

【Udemy】Python入門講座(0からPythonの基礎を体系的に学んで、生成AIでバイブコーディング)
受講生3000人ほどの人気講座。Pythonの基礎から生成AI活用までを20時間ほどで一気に学びます。業務で活かせるPythonの基礎知識はしっかりと身につきます。
リストを使うべき場面
以下のような場合はリストが適している。 1. データの追加・削除・変更が必要な場合
# ショッピングカート
cart = []
cart.append("りんご")
cart.append("バナナ")
cart.remove("りんご") # 購入をやめた
2. データの順序を変更する必要がある場合
# スコアのランキング
scores = [85, 92, 78, 95, 88]
scores.sort(reverse=True) # 降順にソート
print(scores) # [95, 92, 88, 85, 78]
3. 動的にサイズが変わるデータを扱う場合
# ログメッセージを蓄積
log_messages = []
def log(message):
log_messages.append(message)
log("アプリケーション開始")
log("ユーザーログイン: user123")
log("データ処理完了")

【Udemy】Python入門講座(0からPythonの基礎を体系的に学んで、生成AIでバイブコーディング)
受講生3000人ほどの人気講座。Pythonの基礎から生成AI活用までを20時間ほどで一気に学びます。業務で活かせるPythonの基礎知識はしっかりと身につきます。