Python の * 演算子 (iterable unpacking operator) の使い方

Python の `*` 演算子 (iterable unpacking operator) の使い方まとめ
\# Python 3.6.x を想定

# まとめ

- `*iterable` を評価すると，`iterable` の中身をバラすことができる
- 代入文の左辺 or 仮引数に `*var` を置くと，`var` で「残り全部」を受け取ることができる

```python
>>> *(1,2,3), 4
(1, 2, 3, 4)
>>> first, *rest = (1,2,3,4)    # first = 1, rest = [2,3,4]
>>> first, *rest = *(1,2,3), 4  # first = 1, rest = [2,3,4]

>>> def func(a, *args):
...     print(a)
...     print(args)
>>> func(0, 1, 2)
0
(1, 2)
>>> func(*range(3))
0
(1, 2)
```

以下詳細．

# 評価時（右辺値）

- `*iterable` を評価すると，`iterable` の中身をバラすことができる．
- 利用できる文脈：
    - タプル，リスト，セットをリテラルで生成する．
    - 関数に引数群を渡す．
    - （ほかの文脈，たとえば内包表記では使うことが[できない](https://www.python.org/dev/peps/pep-0448/#variations)．）

## タプル，リスト，セットをリテラルで生成する

`()`, `[]`, `{}` を用いてタプル，リスト，セットをリテラルで生成する際，`*iterable` で `iterable` の中身をバラして“代入”できる．

```python
>>> (*(1,2), 3, *[4], *range(3))
(1, 2, 3, 4, 0, 1, 2)
>>> [*(1,2), 3, *[4], *range(3)]
[1, 2, 3, 4, 0, 1, 2]
>>> {*(1,2), 3, *[4], *range(3)}
{0, 1, 2, 3, 4}
>>> *(1,2), 3, *[4], *range(3)
(1, 2, 3, 4, 0, 1, 2)
```

**補足：式のリスト**
なお，最後の例は`()` を省略したタプルの生成記法であり，[式のリスト](https://docs.python.org/ja/3/reference/expressions.html#expression-lists) (“tuple packing”) と呼ばれる．式のリストでシングルトンの（要素がひとつの）タプルを生成したい場合は，`()` を用いるときと同様，末尾に `,` を置く．

```python
# tuples
>>> (1,2)
(1, 2)
>>> (1,) # singleton
(1,)
>>> (1) 
1

# expression lists generating tuples
>>> 1,2
(1, 2)
>>> 1, # singleton
(1,)
>>> 1
1

# iterable unacking in singleton
>>> (*'ab',)
('a', 'b')
>>> (*'ab')
SyntaxError: can’t use starred expression here

>>> *'ab',
('a', 'b')
>>> *'ab'
SyntaxError: can’t use starred expression here
```

## 関数に引数群を渡す

`iterable` から `x`, `y`,  `z` を順に取り出せるとき，`func(*iterable)` は `func(x,y,z)` と等価．

```python
>>> def func(x,y,z):
...     return x + y + z

>>> func(1,3,5)
9
>>> t = (1,3,5)
>>> func(*t)
9
>>> func(1, *(3,5))
9

>>> func(t)
TypeError: func() missing 2 required positional arguments: 'y' and 'z'
```

可変長引数を受け取る関数の場合も同様．

```python
>>> print('foo', 2, True)
foo 2 True

>>> print(*[1,2,3])
1 2 3
>>> print(*'abc')
a b c
>>> print(*(x**2 for x in range(5)))
0 1 4 9 16

>>> print(*'abc', True, *range(3))
a b c True 0 1 2
```

# 代入時（左辺値）
- `*var` に代入すると，`var` が「残り全部」が受け取ることになる．
- 利用できる文脈：
    - 代入文左辺の「変数リスト」内でイテラブルの一部を受け取る．
    - 可変長引数を受け取る関数を作る．

## 代入文左辺の「変数リスト」内でイテラブルの一部を受け取る

代入文の左辺にカンマ区切りの「変数リスト」を置くと，右辺のイテラブルの中身が順に左辺の変数たちに代入される．
変数リストは `()` や `[]` で囲まれていても良い．

```python
>>> a, b, c = (x**2 for x in range(3))
>>> a
0
>>> b
1
>>> c
4

>>> (a, b, c) = (x**2 for x in range(3)) # a=0, b=1, c=4
>>> [a, b, c] = (x**2 for x in range(3)) # a=0, b=1, c=4
```

代入文の左辺の「変数リスト」内に `*` からはじまる変数を置くと，右辺のイテラブルの「残りの要素全部」が当該の変数に**リスト**として代入される (extended iterable unpacking)．

```python
>>> first, *rest = range(5)
>>> first
0
>>> rest
[1, 2, 3, 4]

>>> *rest, last = range(5)
>>> rest
[0, 1, 2, 3]
>>> last
4

>>> first, *rest, last = range(5)
0
>>> rest
[1, 2, 3]
>>> last
4
```

```python
>>> first, *rest, last = 'ab'
>>> first
'a'
>>> rest
[]
>>> last
'b'
```

式のリスト（右辺）と extended iterable unpacking（左辺）の併用：

```python
>>> first, *rest = *(1,2,3,4), 5
>>> first
1
>>> rest
[2, 3, 4, 5]
```

## 可変長引数を受け取る関数を作る
- `*` からはじまる仮引数は可変長引数を受け取ることができる．仮引数名は `args` とするのが慣例．
- 当該の仮引数は，関数内部では**タプル**．

```python
>>> def func(*args):
...     # `args` is a tuple
...     return args

>>> func(1, 2, 3)
(1, 2, 3)
>>> func('abc', 42, True)
('abc', 42, True)
>>> func(*'abc')
('a', 'b', 'c')
```

# おまけ

`*iterable` で `iterable` をバラしたいシーン × 2

## `zip(*iterable)` による unzip/transpose

```python
>>> for t in zip(range(3), 'abc'):
...     print(t)
(0, 'a')
(1, 'b')
(2, 'c')

# unzip/transpose iter_of_iter by zip(*iter_of_iter)
>>> for it in zip(*[(0, 'a'), (1, 'b'), (2, 'c')]):
...     print(it)
(0, 1, 2)
('a', 'b', 'c')
```

## iterable unpacking は内包表記では使うことが[できない](https://www.python.org/dev/peps/pep-0448/#variations)

```python
>>> [*it for it in [[0,1,2], [3], [4,5]]]
SyntaxError: iterable unpacking cannot be used in comprehension

# cf.
>>> def flatten(iter_of_iter):
...     "Flatten one level of nesting"
...     # via. itertools recipes
...     # https://docs.python.org/ja/3/library/itertools.html
...     return itertools.chain.from_iterable(iter_of_iter)
>>> list(flatten([[0,1,2], [3], [4,5]]))
[0, 1, 2, 3, 4, 5]
```


# 参考資料
- 評価時（右辺値）
    - [PEP 448 -- Additional Unpacking Generalizations](https://www.python.org/dev/peps/pep-0448/)
        - [What's New In Python 3.5](https://docs.python.org/ja/3/whatsnew/3.5.html#pep-448-additional-unpacking-generalizations)
    - 関数に引数群を渡す
        - [Python 言語リファレンス » 6.3.4. 呼び出し (Calls)](https://docs.python.org/ja/3/reference/expressions.html#calls)
        - [Python チュートリアル » 4.7.4. 引数リストのアンパック (Unpacking Argument Lists)](https://docs.python.org/ja/3/tutorial/controlflow.html#unpacking-argument-lists)
    - タプル，リスト，セットをリテラルで生成する
        - [Python 言語リファレンス » 6.2.3. 丸括弧形式 (Parenthesized forms)](https://docs.python.org/ja/3/reference/expressions.html#parenthesized-forms)
        - [Python 言語リファレンス » 6.14. 式のリスト (Expression lists)](https://docs.python.org/ja/3/reference/expressions.html#expression-lists)
        - [Python 言語リファレンス » 6.2.5. リスト表現 (List displays)](https://docs.python.org/ja/3/reference/expressions.html#list-displays)
        - [Python 言語リファレンス » 6.2.6. 集合表現 (Set displays)](https://docs.python.org/ja/3/reference/expressions.html#set-displays)
- 代入時（左辺値）
    - 可変長引数を受け取る関数を作る
    - 代入文の左辺でイテラブルの一部を受け取る
        - [Python 言語リファレンス » 7.2. 代入文 (Assignment statements)](https://docs.python.org/ja/3/reference/simple_stmts.html#assignment-statements)
        - [PEP 3132 -- Extended Iterable Unpacking](https://www.python.org/dev/peps/pep-3132/)
        - [python - Unpacking, extended unpacking, and nested extended unpacking - Stack Overflow](https://stackoverflow.com/questions/6967632/unpacking-extended-unpacking-and-nested-extended-unpacking)
- ほか
    - [PEP 3113 -- Removal of Tuple Parameter Unpacking](https://www.python.org/dev/peps/pep-3113/)