Python 3 インデクサとシークエンスのアンパック（アンパック代入）

Python 3では`tuple`や`list`の分割にインデクサと[シークエンスのアンパック（アンパック代入）](https://docs.python.org/ja/3/tutorial/datastructures.html#tuples-and-sequences)の2通りが使えます。後者は慣例的なアンダースコア`_`による変数の破棄（`_`の使い回し）や[アスタリスク`*`によるイテラブルの残った要素のリストへの代入](https://docs.python.org/ja/3/reference/simple_stmts.html#assignment-statements)が可能です。

なお、ここではPython 3ドキュメントにあるタプルへのアンパック以外、リストへの変換もコード中で左辺の要素の一部が変数として利用できればアンパックとみなしています。

```py
[a, b] = 0, 1
# 上式はタプルからリストへの変換だが、リストの要素bは変数として使える
print(b)  # 1
b = 2
print(b)  # 2
print((a, b))  # (0, 2)
```

# 前提条件

## インデクサが使える型と使えない型

インデクサが使える型は`__index__`抽象メソッドを持つクラスであり、使えない型は`__index__`を持たない型です。前者には`tuple`や`list`のようなインデックスが意味を持つ型があり、後者には`set`や`frozenset`のようにインデックスが意味を持たない型です。後者にインデクサを使うと`TypeError`エラーが発生します。

```py
tuple1 = (0, 1, 2)
t0 = tuple1[0]
print(0)  # 0
t0, t1, t2 = tuple1
print((t0, t1, t2))  # (0, 1, 2)

set1 = {0, 1, 2}
# s0 = set1[0]  # TypeError 'set' object is not subscriptable
# print(s0)
s0, s1, s2 = set1
print((s0, s1, s2))  # (0, 1, 2)
```

`__index__`を持つ型は[typing.SupportsIndex](https://docs.python.org/ja/3/library/typing.html#typing.SupportsIndex)として定義されています。

## シークエンスのアンパック（アンパック代入）が使える型と使えない型

[シークエンスのアンパックが使える型は`sequence`に該当する型](https://docs.python.org/ja/3/glossary.html#term-sequence)です。これは`__getitem__`特殊メソッドと`__len__`特殊メソッドを持つ型であり、`tuple`、`list`、`set`等の多くの型が該当します。

## インデクサもシークエンスのアンパックも使える型と使えない型

インデクサもシークエンスのアンパックも使える型は`__index__`、`__getitem__`、`__len__`を持つ型です。`tuple`や`list`はこれに該当しますが、`set`や`frozenset`は該当しません。

# `tuple`の分割

前後の`(`、`)`が省略できるため、等号の左辺（アンパック先）の場合は分割して代入するように書けます。

```py:インデクサ
t = ("a", "b", "c")
a = t[0]
b = t[1]
c = t[2]
print((a, b, c))  # ('a', 'b', 'c')
```

```py:tuple→tupleのアンパック
# tuple→tuple
a, b, c = "a", "b", "c"
print((a, b, c))  # ('a', 'b', 'c')

# tuple→tuple
a, b, c = ("a", "b", "c")
print((a, b, c))  # ('a', 'b', 'c')

# tuple→tuple
(a, b, c) = ("a", "b", "c")
print((a, b, c))  # ('a', 'b', 'c')

# 不要な変数の省略
(_, b, _) = ("a", "b", "c")
print((_, b, _))  # ('c', 'b', 'c')

# 残りをlistにまとめる
(a, *bc, _, e) = ("a", "b", "c", "d", "e")
print((a, bc, _, e))  # ('a', ['b', 'c'], 'd', 'e')
```
```py:tuple→listのアンパック
# tuple→list
[a, b, c] = ("a", "b", "c")
print((a, b, c))  # ('a', 'b', 'c')

# 不要な変数の省略
[_, b, _] = ("a", "b", "c")
print((_, b, _))  # ('c', 'b', 'c')

# 残りをlistにまとめる
[a, *bc, _, e] = ("a", "b", "c", "d", "e")
print((a, bc, _, e))  # ('a', ['b', 'c'], 'd', 'e')
```

# `list`の分割

基本的に`tuple`と同様に扱えます。ただし、前後の`[`、`]`は省略できません。

```py:インデクサ
t = ["a", "b", "c"]
a = t[0]
b = t[1]
c = t[2]
print((a, b, c))  # ('a', 'b', 'c')
```

```py:list→tupleのアンパック
# list→tuple
a, b, c = ["a", "b", "c"]
print((a, b, c))  # ('a', 'b', 'c')

# list→tuple
(a, b, c) = ["a", "b", "c"]
print((a, b, c))  # ('a', 'b', 'c')

# 不要な変数の省略
(_, b, _) = ["a", "b", "c"]
print((_, b, _))  # ('c', 'b', 'c')

# 残りをlistにまとめる
(a, *bc, _, e) = ["a", "b", "c", "d", "e"]
print((a, bc, _, e))  # ('a', ['b', 'c'], 'd', 'e')
```
```py:list→listのアンパック
# list→list
[a, b, c] = ["a", "b", "c"]
print((a, b, c))  # ('a', 'b', 'c')

# 不要な変数の省略
[_, b, _] = ["a", "b", "c"]
print((_, b, _))  # ('c', 'b', 'c')

# 残りをlistにまとめる
[a, *bc, _, e] = ["a", "b", "c", "d", "e"]
print((a, bc, _, e))  # ('a', ['b', 'c'], 'd', 'e')
```

# `set`の分割
インデクサは非対応です。タプルやリストにアンパックする場合、順序は保証されません。

```py:setのアンパック
# set→tuple
# 順序は保証されない。
a, b, c = {"a", "b", "c"}
print((a, b, c))  # ('a', 'b', 'c')の並び替えのどれか

# set→list
# 順序は保証されない。
[a, b, c] = {"a", "b", "c"}
print((a, b, c))  # ('a', 'b', 'c')の並び替えのどれか

# set→set（できない）
# {a, b, c} = {"a", "b", "c"}
# print((a, b, c))  # ('a', 'c', 'b')
```

# 例
- [Python 3 varsの応用](https://qiita.com/katabamisan/items/e3cd779c9f61a2f986d2)