こんにちは(@t_kun_kamakiri)(‘◇’)ゞ
この記事ではPythonのデータの型の中の「タプル型」について解説します。
まずデータの型についてですが、以下の種類があります。
- 数値型
- 整数
- 浮動小数点
- 複素数
- 文字列
- リスト型
- 辞書型(ハッシュ)
- タプル型
- 集合型
- 論理型
これらすべて完璧に使い方を覚える必要はないと思いますので、この記事では「これくらいは覚えておこう」というものをまとめておきます。
その中でも本記事では「タプル型」について解説します。
基本的なことをまとめておきました。
- Pythonのタプル型とは?
- タプル型とリスト型との違い
- 要素の要素の取り出し
- その他リスト型の関数とメソッド
☟書籍も紹介しておきます。
こちらの本がPython初心者が挫折することなく勉強できる本です。
(本記事のようのPython使用環境と異なりますが、とてもわかりやすいので全く問題ありません)
タプル型とは?
数値型や文字列はそれ自体でひとつのデータ(値)でした。
タプル型は、リスト型や辞書型と同じように複数のデータをひとまとまりに扱うことができます。
まずは、タプル型の書き方から確認しましょう。
タプル型は()で囲み、データを「,(カンマ)」で区切る。
では、実際にコードを書いてみましょう。
1 2 3 | tuple_a = ('kamakiri',32, 'male') print(tuple_a,type(tuple_a),id(tuple_a)) |
要素の値、タイプ、アドレス番号も出力しておきます。
【結果】
1 | ('kamakiri', 32, 'male') <class 'tuple'> 140677348016488 |
このようにタプルはリスト型と同じく、異なる型のデータを格納することができます。
ここまでだと、リスト型とタプル型で違いが無いように思えます。
リスト型とタプル型の違い
リスト型とタプル型の違いを理解するために、リスト型でできることをタプル型でも同じことを行ってみます。
- リスト型で要素を変更する
以下のようにリストを用意して要素を出力してみます。
1 2 3 | list_a = ['kamakiri',32, 'male'] print(list_a,type(list_a), id(list_a)) |
【結果】
1 | ['kamakiri', 32, 'male'] <class 'list'> 140677348312264 |
ここで、要素の値を変更してみます。
1 2 3 | list_a[1] = 100 print(list_a,type(list_a),id(list_a)) |
【結果】
1 | ['kamakiri', 100, 'male'] <class 'list'> 140677348312264 |
リスト型は要素の値を自由に変更することができるのですよね。
ここで注意すべき点は、アドレスを変えずに(オブジェクトを変えずに)要素の値が変更できるという点です。
- タプル型で要素を変更する
もう一度以下のようなタプル型を用意して要素を出力してみます。
1 2 3 | tuple_a = ('kamakiri',32, 'male') print(tuple_a,type(tuple_a),id(tuple_a)) |
【結果】
1 | ('kamakiri', 32, 'male') <class 'tuple'> 140677348016488 |
ここで、要素の値を変更してみます。
1 2 3 | tuple_a[1] = 100 print(tuple_a,type(tuple_a),id(tuple_a)) |
【結果】
1 2 3 4 5 6 7 8 | --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-10-a1c6ef365ae1> in <module>() ----> 1 tuple_a[1] = 100 2 3 print(tuple_a,type(tuple_a),id(tuple_a)) TypeError: 'tuple' object does not support item assignment |
エラーが出てきます。
これは、タプル型では要素の値を変更できないということです。
これがリスト型とタプル型の違いのひとつです。
しかし、勘違いしてはいけないのは、アドレスを変えれば(オブジェクトを変えれば)要素の値が変更できるということです。
実際にコードを書いてみましょう。
1 2 3 4 5 | #インデックス1の値を100に変更(新しくタプルを作成) tuple_a = ('kamakiri', 100 , 'male') print(tuple_a,type(tuple_a),id(tuple_a)) |
【結果】
1 | ('kamakiri', 100, 'male') <class 'tuple'> 140677347667904 |
エラー無く要素を出力することができました。
そして、よく見るとアドレスが「140677348016488」から「140677347667904」に変わっています。
このようにタプル型は違うアドレスに変数を代入するようにすれば(違うオブジェクトを作り直す)、要素の値は違うものを格納できるのです。
もちろんこれはリスト型でもできます。
絵にするの↑こんな感じです。
要するに、タプル型は箱の中身(要素)は直接変更することはできないけど、違う箱さえ用意すれば新しい要素を作成することができるということです。
そして、Pythonのタプルはオブジェクトを変えずに要素を変更したり削除したりすることはできません。
タプル型の要素の取り出し
タプル型の要素の取り出しは、リスト型と同じなのでリスト型の記事をご参考ください。
タプル型の要素の取り出し方法の記述は以下です。
1 2 3 4 5 6 | tuple_a = ('kamakiri',32, 'male','Physics') print(tuple_a[1]) print(tuple_a[:]) print(tuple_a[1:]) print(tuple_a[:-2]) |
【結果】
1 2 3 4 | 32 ('kamakiri', 32, 'male', 'Physics') (32, 'male', 'Physics') ('kamakiri', 32) |
リスト型と同じ書き方ですね(^^)
タプル型のメソッド
タプル型のメソッドはリスト型に比べると驚くほど少ないです。
※リスト型で使うメソッド(公式ドキュメント)としては以下のものがありますが、タプル型はその中でも黄色でハイライトした
「indexとcount」だけです。
append | リストの末尾に要素を一つ追加 |
extend | リストに別のリストを追加 |
insert | 指定した位置に要素を追加 |
remove | 要素の値を削除 |
pop | 指定した位置に要素を追加 |
index | リストのインデックスを知りたい |
count | リスト内の要素の数を知りたい |
sort | リスト内の要素の並べ替え |
copy | リストの要素をコピーする |
clear | リスト中の全ての要素を削除 |
reverse | リストの要素を、インプレース演算で逆順にします。 |
タプル型のメソッドは少ないですが、使い方を確認しておきましょう。
しかし、「indexとcount」メソッドはリスト型と同じであるため、リスト型を学んでいる人にとっては目新しいことではないため、復習として見てもらえればと思います。
- indexメソッド
1tuple_a.index(x[, start[, end]])
リスト中で、値 x を持つ最初の要素の位置をゼロから始まる添字で返します。 該当する要素がなければ、ValueError を送出します。docs.python.jp より引用
実際にコードを書いてみましょう。
1 2 3 | tuple_a = ('kamakiri',32, 'male','Physics') print(tuple_a.index('Physics')) |
【結果】
1 | 3 |
‘Physics’という要素がインデックス3にあるので「3」という数字が出力されました。
では、以下のように要素にはないものを指示してみます。
1 | print(tuple_a.index('apple')) |
【結果】
1 2 3 4 5 6 | --------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-24-4c2e09cc4695> in <module>() ----> 1 print(tuple_a.index('apple')) ValueError: tuple.index(x): x not in tuple |
エラーメッセージが返ってきました。
- countメソッド
1tuple_a.count(x)
タプルでの x の出現回数を返します。
実際にコードを書いてみましょう。
1 2 3 4 | #count()メソッド tuple_b = ('商品B','商品A','商品A','商品C','商品B','商品B','商品A','商品A','商品C','商品B','商品B','商品A','商品A','商品C','商品B') tuple_b.count('商品A') |
「’商品A’」という要素が何個あるかを数えることができます。
【結果】
1 | 6 |
ちなみに、タプル同士の足し算はどういう結果になるのかもついでに見ておきましょう。
1 2 3 4 5 6 7 8 | # タプル同士の足し算はできる x = ("A", "B", "C") y = ("D", "E") z = x + y print(z) |
【結果】
1 | ('A', 'B', 'C', 'D', 'E') |
これもリスト型と同じでした(^^)
タプル型の方がリスト型よりパフォーマンスが良い
Pythonで変数のメモリサイズを確認する方法を覚書として書いておきます。
Pythonの標準モジュールの「sys」を使って、引数のサイズをバイト数を確認しておきます。
リスト型のバイト数
1 2 3 4 5 | import sys list_b = ['商品B','商品A','商品A','商品C','商品B','商品B','商品A','商品A','商品C','商品B','商品B','商品A','商品A','商品C','商品B'] print("リスト型:",sys.getsizeof(list_b)) |
【結果】
1 | リスト型: 184 |
タプル型のバイト数
1 2 3 4 5 | import sys tuple_b = ('商品B','商品A','商品A','商品C','商品B','商品B','商品A','商品A','商品C','商品B','商品B','商品A','商品A','商品C','商品B') print("タプル型:",sys.getsizeof(tuple_b)) |
【結果】
1 | タプル型: 168 |
タプル型の方が少ないメモリでデータを格納できているのがわかります。
パフォーマンス観点として、裏でどのような挙動をしているのかを dis()
で確認したいと思います。
dis モジュールは CPython バイトコード bytecode を逆アセンブルすることでバイトコードの解析をサポートします。 このモジュールが入力として受け取る CPython バイトコードはファイル Include/opcode.h に定義されており、 コンパイラとインタプリタが使用しています。
ref:dis — Python バイトコードの逆アセンブラ
リスト型
1 2 3 4 5 6 7 8 | import dis def list_func(): list_b = ['商品B','商品A','商品A','商品C','商品B','商品B','商品A','商品A','商品C','商品B','商品B','商品A','商品A','商品C','商品B'] element_list1 = list_b[1] element_list2 = list_b[2] dis.dis(list_func) |
【結果】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 4 0 LOAD_CONST 1 ('商品B') 2 LOAD_CONST 2 ('商品A') 4 LOAD_CONST 2 ('商品A') 6 LOAD_CONST 3 ('商品C') 8 LOAD_CONST 1 ('商品B') 10 LOAD_CONST 1 ('商品B') 12 LOAD_CONST 2 ('商品A') 14 LOAD_CONST 2 ('商品A') 16 LOAD_CONST 3 ('商品C') 18 LOAD_CONST 1 ('商品B') 20 LOAD_CONST 1 ('商品B') 22 LOAD_CONST 2 ('商品A') 24 LOAD_CONST 2 ('商品A') 26 LOAD_CONST 3 ('商品C') 28 LOAD_CONST 1 ('商品B') 30 BUILD_LIST 15 32 STORE_FAST 0 (list_b) 5 34 LOAD_FAST 0 (list_b) 36 LOAD_CONST 4 (1) 38 BINARY_SUBSCR 40 STORE_FAST 1 (element_list1) 6 42 LOAD_FAST 0 (list_b) 44 LOAD_CONST 5 (2) 46 BINARY_SUBSCR 48 STORE_FAST 2 (element_list2) 50 LOAD_CONST 0 (None) 52 RETURN_VALUE |
要素がどのようにスタックされているのかがわかります。
リスト型は要素をひとつひとつスタックしています。
一方、タプル型どうなっているのでしょうか?
タプル型
1 2 3 4 5 6 7 8 | import dis def tuple_func(): tuple_b = ('商品B','商品A','商品A','商品C','商品B','商品B','商品A','商品A','商品C','商品B','商品B','商品A','商品A','商品C','商品B') element_tuple1 = tuple_b[1] element_tuple2 = tuple_b[2] dis.dis(tuple_func) |
【結果】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 4 0 LOAD_CONST 6 (('商品B', '商品A', '商品A', '商品C', '商品B', '商品B', '商品A', '商品A', '商品C', '商品B', '商品B', '商品A', '商品A', '商品C', '商品B')) 2 STORE_FAST 0 (tuple_b) 5 4 LOAD_FAST 0 (tuple_b) 6 LOAD_CONST 4 (1) 8 BINARY_SUBSCR 10 STORE_FAST 1 (element_tuple1) 6 12 LOAD_FAST 0 (tuple_b) 14 LOAD_CONST 5 (2) 16 BINARY_SUBSCR 18 STORE_FAST 2 (element_tuple2) 20 LOAD_CONST 0 (None) 22 RETURN_VALUE |
タプル型はリスト型のように別々にスタックしているのではなく、まとめてスタックしているのがわかります。
まとめ
タプル型をリスト型と比較しながら解説をしました。
タプル型は箱の中身(要素)は直接変更することはできないけど、違う箱さえ用意すれば新しい要素を作成することができる
そして、Pythonのタプルはオブジェクトを変えずに要素を変更したり削除したりすることはできません。
リスト型で使うメソッド(公式ドキュメント)としては以下のものがありますが、タプル型はその中でも黄色でハイライトした
「indexとcount」だけです。
append | リストの末尾に要素を一つ追加 |
extend | リストに別のリストを追加 |
insert | 指定した位置に要素を追加 |
remove | 要素の値を削除 |
pop | 指定した位置に要素を追加 |
index | リストのインデックスを知りたい |
count | リスト内の要素の数を知りたい |
sort | リスト内の要素の並べ替え |
copy | リストの要素をコピーする |
clear | リスト中の全ての要素を削除 |
reverse | リストの要素を、インプレース演算で逆順にする |
Pythonの記述についてもっと学びたいという方には以下の参考書をお勧めします。
初心者でも挫折することなく学ぶことができます。
Pythonの辞書的な参考書としては以下の参考書をお勧めします。