前回までの「Python初体験」の続きです。

今回はタプル(tuple-型宣言は必要ありませんが...)をやりました。タプルはリスト(list)と同じ、複数のデータの組み合わせなのですが、リストが可変長、要素の値が可変なのに対して、タプルは定義時に長さも要素も固定されます。(C++をやる人なら、constのイメージで考えていただければよいかなと。)
また、表記方法も異なります。

【リスト】
list_sample = [abc, def, ghi]

【タプル】
tuple_sample = abc, def, ghi  #実際のプログラミングでは、
tuple_sample = (abc, def, ghi)  #間違えないように"()"で括られます。

リストとタプルの特性からどのように使い分けるかについて、Python初体験では、特定の生徒の教科別得点を記録する場合(データ記録用という意味が強いので)タプル、(生徒数が異なる等)教科毎に(動的)データを処理する可能性があるような場合にはリスト、というような説明をしています。

「複数の要素から構成される独立したデータ をあらわすときは、タプルを使用します。」
「固定的な形式をもつ独立したデータではなく、不定個数の独立したデータをたくさん集約してまとめておきたい、という用途には、リストが適しています。」

タプルの使い方がいまいちピンときませんが、色々と遊んでいると「んん」と感じることもあります。

value1 = (1, 2, 3)
value2 = (1, 2, 3, 4)
value3 = (2, 3, 4)
value4 = (2, 3, 4, 5)
value5 = (3, 4, 5)
value6 = (3, 4, 5, 6)
value7 = (4, 5, 6)
value8 = (4, 5, 6, 7)

value_a = value1 + value2 + value3 + value4
value_b =  value5 + value6 + value7 + value8

print("value1, value2, value3, value4 : ", value1, value2, value3, value4)
print("value5, value6, value7, value8 : ", value5, value6, value7, value8)
print("value_a[0], value_a[1], value_a[2], value_a[3] : ", value_a[0], value_a[1], value_a[2], value_a[3], )
print("value_b[0], value_b[1], value_b[2], value_b[3] : ", value_b[0], value_b[1], value_b[2], value_b[3], )

等号演算子(==)や不等号演算子(!=)が使えますが、要素数と要素が全く同じ出ないとなりません。比較演算子は次のように判断されるそうです。
「タプル同士の値の比較は、先頭の要素から順番に同じインデックス同士の値を比較して、先に小さい値となったタプルが小さい値となります。」
「比較するタプル同士の長さが異なる場合、短いタプルの要素と長いタプルの要素を比較してすべて等しければ、短い要素のほうが小さい値となります。」

print("value2 > value1? : ", value2 > value1)
print("value3 > value1? : ", value3 > value1)
print("value_a == value1 + value2 + value3 + value4 ? :", value_a == value1 + value2 + value3 + value4)
print("value_b == value5 + value6 + value7 + value8 ? :", value_b == value5 + value6 + value7 + value8)

print("value1 in value_a? : ", value1 in value_a)
mix = (1, 2, 3, "A", "B", "C", 'a', 'b', 'c')
print("mix : ", mix)
print("mix > value1 :", mix > value1)

【出力】

value1, value2, value3, value4 :  (1, 2, 3) (1, 2, 3, 4) (2, 3, 4) (2, 3, 4, 5)
value5, value6, value7, value8 :  (3, 4, 5) (3, 4, 5, 6) (4, 5, 6) (4, 5, 6, 7)
value_a[0], value_a[1], value_a[2], value_a[3] :  1 2 3 1
value_b[0], value_b[1], value_b[2], value_b[3] :  3 4 5 3
value2 > value1? :  True
value3 > value1? :  True
value_a == value1 + value2 + value3 + value4 ? : True
value_b == value5 + value6 + value7 + value8 ? : True
value1 in value_a? :  False
mix :  (1, 2, 3, 'A', 'B', 'C', 'a', 'b', 'c')
mix > value1 : True

二次元のタプルを作るには、このようにするのでしょうか?一次元のタプルを要素にしたタプル配列を作るようです。

single1 = (1, 2, 3, 4, 5)
single2 = ("ABC", "DEF", "GHI", "JKL",)
single3 = (6, "MNO", 7, "PQR")
#multi1 = (,)  #エラー(空の配列ポインターはできない)
multi1 = (single1, single2, single3)
print("multi1 : ", multi1)
print("multi1's elements : ", multi1[0], multi1[1], multi1[2])
multi2 = single1
multi2 += single2
multi2 += single3
print("multi2 : ", multi2)
print("multi2's elements : ", multi2[0], multi2[1], multi2[2], multi2[3], multi2[4], multi2[5], multi2[6], multi2[7], multi2[8], multi2[9], multi2[10], multi2[11], multi2[12])

【出力】
multi1 :  ((1, 2, 3, 4, 5), ('ABC', 'DEF', 'GHI', 'JKL'), (6, 'MNO', 7, 'PQR'))
multi1's elements :  (1, 2, 3, 4, 5) ('ABC', 'DEF', 'GHI', 'JKL') (6, 'MNO', 7, 'PQR')
multi2 :  (1, 2, 3, 4, 5, 'ABC', 'DEF', 'GHI', 'JKL', 6, 'MNO', 7, 'PQR')
multi2's elements :  1 2 3 4 5 ABC DEF GHI JKL 6 MNO 7 PQR

最初から要素だった場合(multi1)と、後で'+'演算子で追加された場合(multi2)の取り扱いが異なるようです。multi1は二次配列になっていますが、multi2は一次配列の要素が追加された一次配列になっています。

関連するメソッドとして、以下のようにtupleとlistを変換する関数や、(内部的に変更不可能なtupleを一旦リストに変換してソートしてまたタプルにする)sorted関数があるそうです。

tuple(list)
list(tuple)

tp = (1, 9, 3, 7, 5, 8)
print("タプル tp : ", tp)
lst = sorted(tp)  #ntpはリスト
print("Make a list, lst made of sorted tp : ", lst)
ntp = tuple(lst)  #リストをタプルに変換
print(ntp)

【出力】
タプル tp :  (1, 9, 3, 7, 5, 8)
Make a list, lst made of sorted tp :  [1, 3, 5, 7, 8, 9]
(1, 3, 5, 7, 8, 9)

 

まぁ(リストがあれば足りるのですが、書き換えできないデータを使いたい等の事態もあるでしょうし)、おいおいタプルの必然性が分かってくるでしょう。