Python の *
演算子 (iterable unpacking operator) の使い方まとめ
# Python 3.6.x を想定
まとめ
-
*iterable
を評価すると,iterable
の中身をバラすことができる - 代入文の左辺 or 仮引数に
*var
を置くと,var
で「残り全部」を受け取ることができる
>>> *(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
の中身をバラすことができる. - 利用できる文脈:
- タプル,リスト,セットをリテラルで生成する.
- 関数に引数群を渡す.
- (ほかの文脈,たとえば内包表記では使うことができない.)
タプル,リスト,セットをリテラルで生成する
()
, []
, {}
を用いてタプル,リスト,セットをリテラルで生成する際,*iterable
で iterable
の中身をバラして“代入”できる.
>>> (*(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)
補足:式のリスト
なお,最後の例は()
を省略したタプルの生成記法であり,式のリスト (“tuple packing”) と呼ばれる.式のリストでシングルトンの(要素がひとつの)タプルを生成したい場合は,()
を用いるときと同様,末尾に ,
を置く.
# 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)
と等価.
>>> 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'
可変長引数を受け取る関数の場合も同様.
>>> 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
が「残り全部」が受け取ることになる. - 利用できる文脈:
- 代入文左辺の「変数リスト」内でイテラブルの一部を受け取る.
- 可変長引数を受け取る関数を作る.
代入文左辺の「変数リスト」内でイテラブルの一部を受け取る
代入文の左辺にカンマ区切りの「変数リスト」を置くと,右辺のイテラブルの中身が順に左辺の変数たちに代入される.
変数リストは ()
や []
で囲まれていても良い.
>>> 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).
>>> 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
>>> first, *rest, last = 'ab'
>>> first
'a'
>>> rest
[]
>>> last
'b'
式のリスト(右辺)と extended iterable unpacking(左辺)の併用:
>>> first, *rest = *(1,2,3,4), 5
>>> first
1
>>> rest
[2, 3, 4, 5]
可変長引数を受け取る関数を作る
-
*
からはじまる仮引数は可変長引数を受け取ることができる.仮引数名はargs
とするのが慣例. - 当該の仮引数は,関数内部ではタプル.
>>> 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
>>> 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 は内包表記では使うことができない
>>> [*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
- 関数に引数群を渡す
- タプル,リスト,セットをリテラルで生成する
- 代入時(左辺値)
- ほか