タプル(tuple)#

Jupyter Notebook ファイルの準備

このノートを学習するために、「tuple.ipynb」 という名前で Jupyter Notebook のファイルを作成してください。

このノートの練習問題

内容を一通り確認できたら、以下の練習問題に取り組むことでご自身の理解度をチェックしてください。
練習問題:練習問題:タプル | データ構造

タプルはリストと同じく、複数の要素をもち、かつそれらの要素が順番に一列で並んだデータ構造をもつオブジェクトです。データ構造は似ていますが、リストとタプルには次の点で大きな違いがあります。リストが要素を後から追加したり、更新したり、削除したりできる「可変」な性質をもつのに対し、タプルはそうした後からの変更が一切できない「不変」な性質をもちます。このような性質の違いに着目して、タプルとその操作方法を説明します。

1. タプルの基本操作#

1-1. タプルを作成する#

タプルは、次のように () を用いたリテラルで作成できます。リストと同様に () 内にはカンマ区切りで要素を順番に並べて記述します。

(要素1, 要素2, 要素3, ...,)
(要素1, 要素2, 要素3, ...)  # 最後のカンマは省略できる 
(要素1, )  # ただし要素が 1 つの場合は最後のカンマは省略できない
() # 空のタプル
要素1, 要素2, 要素3, ...  # 記述する場所によっては () を省略してもタプルとみなされる

要素が 1 つの場合は (0, ) のように書き、最後の , は省略できません。(0) と書くと、式の評価を優先するための囲みで使う () とみなされてしまいまいます。

tup_1 = (1, 2.0, 'a', True)  # 4 つの要素をもつタプル(各要素は異なる型でもOK)
tup_2 = (1, )  # 1 つだけ要素をもつタプル
num = (1)  # これは単なる整数値の 1 であり、() は式の評価の優先度を上げる () と見なされる
tup_empty = () # 空のタプル
tup_3 = 1, 2.0, 'a', True  # (1, 2.0, 'a', True) と同じとみなされる

print(tup_1)      # (1, 2.0, 'a', True)
print(tup_2)      # (1,)
print(num)        # 1
print(tup_empty)  # ()
print(tup_3)  # (1, 2.0, 'a', True)
(1, 2.0, 'a', True)
(1,)
1
()
(1, 2.0, 'a', True)

また、() を省略できるケースもありますが、基本は () で囲むことを推奨します。例えば以下のコードは、変数 num に整数値の 1 を、変数 tup(2, 3, 4) のタプルを代入できるようにも見えますが、実行すると例外が送出されてしまいます。

num, tup = 1, 2, 3, 4
print(num, tup)

もちろん () を書いておけば、意図した通りに動作します。このようなミスをしないためにも、慣れないうちは常に () で囲むようにしてください。

num, tup = 1, (2, 3, 4)
print(num, tup)

1-2. タプルの長さを取得する#

タプルの長さ(要素数)も、リストと同様に len 関数で調べることができます。

tup = (0, 1, 2, 3)
print(len(tup))  # 4
4

1-3. タプルの要素を取得する#

タプルも要素が順番に一列で並んでいるため、ブラケット表記法で [] 内に先頭を 0 で表すインデックスを指定して、各要素を取得できます。インデックスとして、負の値を指定したときの取得ルールもリストと同様です。

tup = (0, 1, 2, 3)

print(tup[2])   # インデックスが 2 の要素を取得
print(tup[-1])  # 末尾の要素を取得
2
3

また、タプルもスライスを用いて、要素をまとめて取得することができます。表記のルールもリストと同じで [開始位置, 終了位置, ステップ] です。ただし、スライスを用いても要素の変更や削除はできないので気を付けてください

tup = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

# インデックスが 3 ~ 6 の要素を取得
print(tup[3:7])  # (3, 4, 5, 6) ※ 7 は含まれないので注意

# インデックスが 4 以上の要素を取得
print(tup[4:])  # (4, 5, 6, 7, 8, 9)

# インデックスが 4 未満の要素を取得
print(tup[:4])  # (0, 1, 2, 3)

# インデックス 4 から末尾まで 2 つおきに要素を取得
print(tup[4::2])  # (4, 6, 8)

# 先頭からインデックス 7 未満まで 2 つおきに要素を取得
print(tup[:7:2])  # (0, 2, 4, 6)

# 先頭から末尾まで 2 つおきに要素を取得
print(tup[::2])  # (0, 2, 4, 6, 8)
(3, 4, 5, 6)
(4, 5, 6, 7, 8, 9)
(0, 1, 2, 3)
(4, 6, 8)
(0, 2, 4, 6)
(0, 2, 4, 6, 8)

1-4. タプルは要素の更新や削除ができない#

タプルは一度作成されると、後からの変更が一切できない不変な性質をもつオブジェクトです。そのため、代入演算子で要素を更新しようとしたり、del 命令で削除しようとしたりすると例外(TypeError)が送出されます。

  • タプルの要素を更新しようとする例

    tup = (0, 1, 2, 3)
    # 以下を有効にすると、TypeError が送出される
    # tup[0] = 4
    
  • タプルから要素を削除しようとする例

    tup = (0, 1, 2, 3)
    # 以下を有効にすると、TypeError が送出される
    # del tup[0]
    

1-5. タプルから繰り返し要素を取得する#

タプルも繰り返し可能オブジェクトです。よって、タプルがもつ要素を順番に取得したい場合は、リストと同じく for 文を利用します。

tup = (0, 1, 2, 3)

for element in tup:
    print(element)
0
1
2
3

2. メソッドを利用したタプルの操作#

タプルはリストと異なり、不変な性質をもつことから、メソッドが次の 2 つだけしかありません。

  • count(obj): タプル内で obj と等しい値をもつ要素をすべて調べて、その個数を返します。

  • index(obj): タプル内で最初に obj と等しい値をもつ要素を先頭から探索して、最初に見つかった要素のインデックスを返します。同じ値を持つ要素が見つからない場合は ValueError を送出します。

tup = ('a', 'b', 'c', 'd', 'c', 'c')

print(tup.count('c'))  # 3 ← 'c' は3個ある
print(tup.index('c'))  # 2 ← 最初に見つかる 'c' のインデックスは 2
3
2

appendinsert のように要素を追加したり、 popremoveclear のような要素を削除するメソッドはないため気を付けてください。

3. タプルのアンパック#

タプルも、変数名の先頭にアスタリスク(*)を付けることで、要素をその場で展開することができます。各要素をそれぞれ別の変数にまとめて代入したり、複数のタプルをまとめて新しいリストやタプルを作成したりするときに使えます。

  • アンパックして各要素を関数呼び出しの引数として渡す

person = ('湘南ハイテク', '神奈川県', 40)
print(person)   # ('湘南ハイテク', '神奈川県', 40) ← タプルのまま
print(*person)  # 湘南ハイテク 神奈川県 40  ← 各要素が個別に渡される
('湘南ハイテク', '神奈川県', 40)
湘南ハイテク 神奈川県 40
  • 各要素をそれぞれ変数にまとめて代入する

person = ('湘南ハイテク', '神奈川県', 40)
name, birthplace, age = person  # 分割代入では変数名の先頭にアスタリスクは不要
print(name, birthplace, age) 
湘南ハイテク 神奈川県 40
  • 要素を1つのリストにまとめて代入する

tup = (0, 1, 2, 3, 4, 5)
start, *middles, end = tup  # 代入される変数名の先頭に * を付ける
print(start, middles, end)  # 0 [1, 2, 3, 4] 5
# ただし、要素はタプルではなく、リストにまとめられることに注意
print(type(middles))  # <class 'list'>
0 [1, 2, 3, 4] 5
<class 'list'>
  • 2 つのタプルの要素を結合して新しいタプルを作成する

tup1 = (1, 2, 3)
tup2 = (4, 5, 6)
tup3 = (*tup1, *tup2)  # (1, 2, 3, 4, 5, 6) のリテラルと同じ処理が行われる
print(tup3)
(1, 2, 3, 4, 5, 6)

4. タプルと演算子#

タプルもリストと同じく演算子を使って次の操作が行えます。

4-1. + 演算子を用いた連結#

+ 演算子を使うと、2 つのタプルを連結した、新しいタプルを作成できます。

tup1 = (1, 2, 3)
tup2 = (4, 5, 6)
tup_joined = tup1 + tup2
print(tup_joined)  # (1, 2, 3, 4, 5, 6)
(1, 2, 3, 4, 5, 6)

4-2. * 演算子を用いた繰り返しの連結#

* 演算子を使うと、指定されたタプルを繰り返し連結した、新しいタプルを作成できます。

tup = (1, 2, 3)
tup_repeated = tup * 3  # 元のタプルを 3 回繰り返し連結したタプルを作成
print(tup_repeated)     # (1, 2, 3, 1, 2, 3, 1, 2, 3)
(1, 2, 3, 1, 2, 3, 1, 2, 3)

4-3. in 演算子や not in 演算子による要素の有無の判定#

リストと同じように、演算子の左オペランドに指定したオブジェクトと等しい値をもつ要素が、タプルに存在するかどうかを調べることができます。

  • obj in tupleobj と等しい値をもつ要素が tuple にあれば True を返し、なければ False を返します。

  • obj not in tuplein 演算子と反対の結果を返します。

tup = ('a', 'b', 'c')

# in 演算子の使用例
print('a' in tup)  # True
print('e' in tup)  # False

# not in 演算子の使用例
print('a' not in tup)  # False
print('e' not in tup)  # True
True
False
False
True

4-4. == 演算子や != 演算子を用いたタプルどうしの比較#

これらの演算子も 2 つのタプルが等しいかどうかを比較するときに使えます。

  • == : 2 つのタプルが等しい要素を同じ順序でもつかどうかを判定します。

  • !=== と反対の評価をします。

tup1 = (1, 2, 3, 4, 5)
tup2 = (1, 2, 3, 4, 5)  # tup1 と等しい要素を同じ順序でもつ
tup3 = (5, 4, 3, 2, 1)  # tup1 と等しい要素をもつが、順序が違う

print(tup1 == tup2)  # True
print(tup2 == tup3)  # False

print(tup1 != tup2)  # False
print(tup2 != tup3)  # True
True
False
False
True

おわりに#

タプルは不変な性質をもつことから、リストと比べて、より効率良くメモリを使用することができます。そのため、要素の変更がない場合はリストではなく、代わりにタプルを使うことを検討してください。また、タプルにはリストのような内包表記は用意されていません。これらの特長を踏まえた上で、プログラムで扱いたいデータの特性やその用途に合わせて、タプルとリストを適切に使い分けることが大切です。