Rubyistが学ぶPython ~ タプル編 ~



## これはなに
エンジニア歴１年半、メイン言語としてRuby一筋でやってきた自分が、最近Pythonを学び始めたその備忘録です。
今回はPythonのタプルについて勉強してきました。

※以下、Python3についての内容です

## タプルについて
タプルは、配列のように任意の値を集めたシーケンス。
Pythonの配列はミュータブルだけど、**タプルはイミュータブル**。一度定義すると、後から要素を追加したり削除したり変更したりすることはできない。
Rubyにはないデータ型だったので少し戸惑い。（配列にfreezeかけたりして似たようなことはできるよう）

### タプルの生成
`()`と記述することで、空のタプルを生成できる。

```python
new_tuple = ()
# >>> ()
```

要素を持つタプルを記述するときには、以下のように記述する

```python
my_tuple = ('foo', 'bar', 'piyo')
# >>> ('foo', 'bar', 'piyo')
```

なるほど`()`がタプルを定義するのだな...と思いきや、実は`()`なしでもいける。

```python
truth_tuple = 'foo', 'bar', 'piyo'
# >>> ('foo', 'bar', 'piyo')
```

ただ、要素が1つだけのタプルを`()`無しで定義するには、要素の後ろに(その要素の後ろに何も続かなくでも)カンマを付ける必要がある。

```python
single_tuple = 'foo',
# >>> ('foo',)
```

タプルをタプルたらしめるのは`()`でなく`,`。ここ絶対テストでる。
でも、分かりやすくするために`()`で囲っておいたほうが無難だと思う。「暗黙よりも明示がいい」。[Python公案](https://www.python.org/dev/peps/pep-0020/)にもしっかりと書いてある。

また、変換関数の`tuple()`を用いて、配列など他のものからタプルを作成できる。

```python
array = [1, 2, 3, 4, 5]
tuple_from_ary = tuple(array)
# >>> (1, 2, 3, 4, 5)

# Pythonの辞書からは、そのキーを集めたタプルを作成できる
dictionary = { 'message': 'success!', 'status': 'true'}
tuple_from_dic = tuple(dictionary)
# >>> ('message', 'status')
```

### タプルを用いた変数への一括代入
タプルの**アンパック**と呼ばれる方法を用いると、タプルに格納された値を複数の変数に一括で代入できる。

```python
tuple = 'foo', 'hoge', 'piyo'
a, b, c = tuple
print(a, b, c)
# >>> foo bar hoge

# 変数の入れ替えも１行で行える
a, b = b, c
print(a, b)
# >>> bar foo

# 列挙可能オブジェクトをタプルの形式で保持しておけば、
# アンパックを用いたシンプルでエレガントなループ処理が書ける
num_pairs = [(1, 1), (1, 2), (1,3), (2, 1), (2, 2), (2, 3)]
for first, second in num_pairs:
    print(first, second)

# >>> 1 1
# >>> 1 2
# >>> 1 3
# >>> 2 1
# >>> 2 2
# >>> 2 3
```

### タプルへのアクセス
タプルにアクセスするには、配列と同じように先頭からのオフセットを指定する。
タプルの要素の個数を超える数を指定すると、IndexErrorが起きるので注意。

```python
test_tuple = ('hoge', 'fuga', 'piyo')
print(test_tuple[1])
# >>> fuga

# pythonのスライスを用いると複数取得も
print(test_tuple[0:2])
# >>> ('hoge', 'fuga')
```

### タプル同士の結合、値の変更(？)
タプル同士を`+`演算子で繋げると、二つのタプルを結合した新しいタプルオブジェクトを作成することができる。
もちろん、この時それぞれの元のタプルに変更は生じない

```python
tuple_1 = (1, 2, 3)
tuple_2 = (4, 5)

tuple_3 = tuple_1 + tuple_2
print(tuple_3)
# >>> (1, 2, 3, 4, 5)
print(tuple_1)
# >>> (1, 2, 3)
print(tuple_2)
# >>> (4, 5)

# 一見タプルのを変更しているように見える以下のコードも
# 実際は新しいオブジェクトを再代入しているだけなので正常に動作する
tuple_1 += tuple_2 # tuple_1 = tuple_1 + tuple_2
print(tuple_1)
# >>> (1, 2, 3, 4, 5)
```
タプルは要素の追加、削除、変更ができないが、それは**タプルが持つ参照を変更する場合**であり、例えばタプルの一要素の配列の中身を変更したり削除したりといったことは普通に可能。

```python
contain_ary = ([1, 2, 3], [4, 5, 6])

contain_ary[0].append(4)
contain_ary[1].clear()

print(contain_ary)
# >>> ([1, 2, 3, 4], [])
```

純粋な意味でタプルの参照を変更したい場合は、`list()`関数を用いて一度配列オブジェクトに変更してから処理をし、そのあと`tuple`関数で再びtupleに戻す、という方法も取れる。
しかし、そういった変更を前提とするような処理なら最初からタプルじゃなく配列使えよって感じがする。。。

### タプルを用いるメリット

先に述べたとおり、タプルはイミュータブルなデータ型なので、誤って書き換えてしまう心配がない。
また、タプルはPythonの辞書(Rubyでいうハッシュ)のキーとして使うことができるため、何らかの意味を持つデータのペアや組み合わせをキーとした辞書を作ることができる。

```python
dice_pairs = {
  (1, 1): '10点',
  (1, 2): '1点',
  (1, 3): '2点',
    ・
    ・
    ・
  (6, 5): '1点',
  (6, 6): '100点'
}

print(dice_pairs[(1, 2)])
# >>> 1点
```
はえー、便利。

### タプルオブジェクトのメソッド
タプルオブジェクトが呼び出せメソッドは、`index()`と`count()`のみ。それぞれ、引数に与えた値をタプルの中から探し、その最初のオフセットや個数を返す。

``` python
target_tuple = (1, 2, 3, 3, 4, 5)
print(target_tuple.index(5))
# >>> 5

print(target_tuple.count(3))
# >>> 2
```

### その他
Pythonの書式指定(`"my favorite number is %d and %d" % (1, 4)`みたいな。Rubyにもあったよね)など、上記以外でもタプルを用いる場面はたくさんあるので、その性質や扱い方を知っておくことは、Pythonicなエンジニアを目指すなら必須かと思われます。

また、クラス構造をシンプルに表せるという`名前付きタプル`もあるようですが、そちらについてはまたの機会に。



### 参考
- [Python のタプルとリストの違い、タプルの使いどころ](https://www.lifewithpython.com/2017/12/python-tuple-list-difference.html)
- [Pythonでタプルの要素を追加・変更・削除](https://www.lifewithpython.com/2017/12/python-tuple-list-difference.html)
- [Python Tuple Methods](https://www.programiz.com/python-programming/methods/tuple)