Python 入門

Python タプル (Tuple)

タプル(Tuples)は、関連するデータをグループ化するための手法を提供します。
リスト(Lists)とは異なり、タプルはイミュータブル(immutable/不変)です。つまり、一度作成するとその内容を変更することはできません。この特性により、タプルは固定された項目の集合を表現するのに適しており、データの完全性とコードの信頼性を高めることができます。

1. タプルの理解:不変性とユースケース

タプルは、Python オブジェクトの順序付けられたイミュータブルなシーケンスです。通常、丸括弧 () を使用して定義され、要素間はカンマ , で区切られます。タプルの不変性は、その決定的な特徴であり、どのように、そしてどこで使用すべきかに直接影響します。

1.1 タプルの作成

Python では、タプルを作成する方法がいくつかあります。

1. 丸括弧を使用する: これが最も一般的な方法です。カンマで区切られた要素のシーケンスを丸括弧で囲みます。

# 整数型のタプルを作成
my_tuple = (1, 2, 3, 4, 5)
print(my_tuple)  # 出力: (1, 2, 3, 4, 5)

# 混合データ型のタプルを作成
mixed_tuple = (1, "hello", 3.4, True)
print(mixed_tuple)  # 出力: (1, 'hello', 3.4, True)

2. 丸括弧を使用しない(タプルパッキング / Tuple Packing): 2つ以上の項目が含まれる場合、Python では丸括弧なしでタプルを作成できます。これを「タプルパッキング」と呼びます。

# タプルパッキング
my_tuple = 1, 2, "world"
print(my_tuple)  # 出力: (1, 2, 'world')

3. tuple() コンストラクタを使用する:tuple() コンストラクタを使用して、リストや文字列などの他のイテラブルオブジェクトからタプルを作成できます。

# リストからタプルを作成
my_list = [1, 2, 3]
my_tuple = tuple(my_list)
print(my_tuple)  # 出力: (1, 2, 3)

# 文字列からタプルを作成
my_string = "Python"
my_tuple = tuple(my_string)
print(my_tuple)  # 出力: ('P', 'y', 't', 'h', 'o', 'n')

4. 空のタプルを作成: 空の丸括弧のペアを使用して空のタプルを作成します。

# 空のタプルを作成
empty_tuple = ()
print(empty_tuple)  # 出力: ()

5. 単一要素のタプルを作成: これは非常に重要な詳細です! 1つの要素だけを持つタプルを作成するには、要素の後に末尾カンマを付ける必要があります。このカンマがないと、Python はそれを単なる数学的な括弧式として解釈します。

# 単一要素のタプルを作成(カンマに注目)
single_tuple = (42,)
print(single_tuple)  # 出力: (42,)

# これはタプルではなく、単なる括弧付きの整数
not_a_tuple = (42)
print(type(not_a_tuple)) # 出力: <class 'int'>

2. タプル要素へのアクセス

リストへのアクセスと同様に、インデックスを使用してタプル内の要素にアクセスできます。Python ではインデックスは 0 から始まります。

my_tuple = (10, 20, 30, 40, 50)

# 正方向のインデックスで要素にアクセス
print(my_tuple[0])  # 出力: 10
print(my_tuple[3])  # 出力: 40

# 負方向のインデックスでアクセス(末尾から数える)
print(my_tuple[-1]) # 出力: 50
print(my_tuple[-2]) # 出力: 40

# タプルのスライス
print(my_tuple[1:4]) # 出力: (20, 30, 40)
print(my_tuple[:3])  # 出力: (10, 20, 30)
print(my_tuple[3:])  # 出力: (40, 50)

3. タプルの不変性 (Immutability)

タプルが作成されると、その要素を変更することはできません。つまり、タプルに対して要素の追加、削除、または修正を行うことはできません。これを試みると、プログラムは例外をスローしてエラーになります。

my_tuple = (1, 2, 3)

# タプル内の要素を変更しようとする(エラーが発生)
try:
    my_tuple[0] = 10  # TypeError が発生
except TypeError as e:
    print(f"Error: {e}")  # 出力: Error: 'tuple' object does not support item assignment

# タプルに要素を追加しようとする(エラーが発生)
try:
    my_tuple.append(4) # AttributeError が発生
except AttributeError as e:
    print(f"Error: {e}") # 出力: Error: 'tuple' object has no attribute 'append'

4. タプルの基本操作

タプル自体はイミュータブルですが、特定の操作を実行することは可能です(これらの操作は通常、新しいタプルを生成します)。

4.1 連結 (Concatenation)

+ 演算子を使用して2つ以上のタプルを連結し、新しいタプルを作成できます。

tuple1 = (1, 2, 3)
tuple2 = (4, 5, 6)
combined_tuple = tuple1 + tuple2
print(combined_tuple)  # 出力: (1, 2, 3, 4, 5, 6)

4.2 反復 (Repetition)

* 演算子を使用してタプルを繰り返し、重複した要素を含む新しいタプルを作成できます。

my_tuple = ("hello",)
repeated_tuple = my_tuple * 3
print(repeated_tuple)  # 出力: ('hello', 'hello', 'hello')

4.3 メンバーシップテスト

in および not in 演算子を使用して、特定の要素がタプル内に存在するかどうかを確認できます。

my_tuple = (1, 2, 3, 4, 5)
print(3 in my_tuple)    # 出力: True
print(6 in my_tuple)    # 出力: False
print(6 not in my_tuple) # 出力: True

4.4 タプルのアンパック

タプルのアンパックにより、タプル内の要素を一度に複数の独立した変数に割り当てることができます。注意:変数の数は、タプル内の要素数と完全に一致している必要があります。

my_tuple = (1, "hello", 3.4)

# タプルのアンパック
a, b, c = my_tuple
print(a)  # 出力: 1
print(b)  # 出力: hello
print(c)  # 出力: 3.4

# 変数の数が一致しないアンパックを試みる(エラーが発生)
try:
    x, y = my_tuple # ValueError が発生
except ValueError as e:
    print(f"Error: {e}") # 出力: Error: too many values to unpack (expected 2)

# アンパック時に特定の値を無視する
d, _, f = my_tuple # アンダースコア (_) は通常、意図的にこの変数を無視することを示すプレースホルダーとして使われます
print(d) # 出力: 1
print(f) # 出力: 3.4

5. タプルをいつ使用すべきか?

プログラムの実行中にデータが変更されないことを保証する必要があるシナリオでは、タプルが非常に有用です。以下は一般的なユースケースです。

  • 固定されたコレクションの表現: 修正すべきでない項目のセットがある場合に使用します。例:座標 (x, y)、RGB カラー値 (red, green, blue)、または曜日など。
  • 関数から複数の値を返す: 関数は複数の結果を一つのタプルとして返すことができます。これは、関連するデータを返すクリーンで効率的な方法です。
  • 辞書のキー (Keys) としての使用: タプルはイミュータブルであるため、辞書のキーとして使用できます。一方、リストは変更可能であるためキーとして使用できません(辞書のキーはハッシュ可能な不変型である必要があります)。
  • データの完全性: タプルは不変であるため、データの予期せぬ書き換えを防止し、一定レベルのデータセキュリティを提供します。
  • パフォーマンス: 不変性のおかげで、タプルはメモリ使用量や反復速度の面でリストよりもわずかに効率的です。
# 関数が複数の値を返す例
def get_circle_properties(radius):
    """円の面積と円周を計算する。"""
    area = 3.14159 * radius * radius
    circumference = 2 * 3.14159 * radius
    return area, circumference  # ここで返されるのはタプルです

circle_area, circle_circumference = get_circle_properties(5)
print(f"面積: {circle_area}, 円周: {circle_circumference}")
# 出力: 面積: 78.53975, 円周: 31.4159

# タプルを辞書のキーとして使用する例
my_dict = {
    (1, 2): "地点 A",
    (3, 4): "地点 B"
}
print(my_dict[(1, 2)]) # 出力: 地点 A

6. タプルとリストの違い (Tuples vs. Lists)

特性タプル (Tuple)リスト (List)
可変性イミュータブル (変更不可)ミュータブル (変更可能)
構文() 丸括弧[] 角括弧
ユースケース固定された集合、辞書のキー動的な集合、頻繁に修正が必要なデータ
メソッドメソッドが少ない (不変のため)豊富なメソッドを提供 (append, remove など)
パフォーマンス高速、メモリ消費が少ない比較的低速、メモリ消費が多い

7. 実践ケースとデモンストレーション

タプルの理解を深めるために、具体的な例を見てみましょう。

7.1 ケース 1:座標の表現

タプルは、2次元 (2D) または3次元 (3D) 空間の座標点を表現するためによく使われます。

# 2D 座標の表現
point = (10, 20)
print(f"X: {point[0]}, Y: {point[1]}") # 出力: X: 10, Y: 20

# 3D 座標の表現
point_3d = (5, 12, 8)
print(f"X: {point_3d[0]}, Y: {point_3d[1]}, Z: {point_3d[2]}") # 出力: X: 5, Y: 12, Z: 8

7.2 ケース 2:複数の戻り値

前述の通り、関数は複数の値をタプルにまとめて返し、呼び出し側で直接アンパックできます。

def get_min_max(numbers):
    """数値リスト内の最小値と最大値を返す。"""
    if not numbers:
        return None, None  # 空リストの場合の処理
    return min(numbers), max(numbers)

numbers = [3, 1, 4, 1, 5, 9, 2, 6]
min_value, max_value = get_min_max(numbers)
print(f"最小値: {min_value}, 最大値: {max_value}") # 出力: 最小値: 1, 最大値: 9

7.3 ケース 3:辞書のキーとしての利用

タプルを使用して、辞書内の複合キー(複数の条件によるマッピング)を表現できます。

# 辞書でタプルをキーとして使用
student_grades = {
    ("Alice", "Math"): 95,
    ("Bob", "Science"): 88,
    ("Alice", "Science"): 92
}
print(student_grades[("Alice", "Math")]) # 出力: 95

7.4 ケース 4:データバリデーション

タプルを利用して有効なオプションのホワイトリストを保存し、データバリデーションに使用できます(ホワイトリストが誤って改ざんされるのを防ぎます)。

def validate_input(user_input, valid_options):
    """有効なオプションのタプルに基づいてユーザー入力を検証する。"""
    if user_input in valid_options:
        print("入力は有効です。")
    else:
        print("入力は無効です。")

valid_colors = ("red", "green", "blue")
validate_input("red", valid_colors)   # 出力: 入力は有効です。
validate_input("purple", valid_colors) # 出力: 入力は無効です。