トラック
/
Python
Python
/
シラバス
/
アンパックと多重代入
アン

アンパックと多重代入 の Python

3個の演習

アンパックと多重代入について

アンパックとは、list、tuple、dictのようなコレクションの要素を、繰り返しを使って取り出す操作のことです。 アンパックした値は、同じ文の中で変数に代入できます。 この動作のごく一般的な例がfor item in listで、itemは繰り返しのあいだ、listの各要素の値を順番に受け取ります。

多重代入とは、1つの文の中でアンパックした値に複数の変数を代入できる機能です。 これによりコードをより簡潔で読みやすくでき、代入する変数をカンマで区切ってfirst, second, third = (1,2,3)やfor index, item in enumerate(iterable)のように書きます。

特殊な演算子*と**は、アンパックの文脈でよく使われます。 *を使うと、複数のlist/tupleを1つのlist/tupleにまとめられます。それぞれを新しい共通のlist/tupleに_アンパック_するのです。 **を使うと、複数の辞書を1つの辞書にまとめられます。それぞれを新しい共通のdictに_アンパック_するのです。

*演算子をコレクションなしで使うと、いくつかの値をlistに_パック_します。 これは多重代入でよく使われ、個別の代入がない「残り」の要素をすべて1つの変数にまとめます。

Pythonでは、任意の数の位置引数やキーワード引数を取る関数を使ったり定義したりするときにも、このアンパック/パックの動作を活用するのが一般的です。 こうした「特殊な」仮引数がdef some_function(*args, **kwargs)のように定義され、「特殊な」引数がsome_function(*some_tuple, **some_dict)のように使われるのをよく目にします。

Caution

*<variable_name>と**<variable_name>を、*と**と混同しないでください。*と**はそれぞれ掛け算と累乗に使われますが、*<variable_name>と**<variable_name>はパックとアンパックの演算子として使われます。

多重代入

多重代入では、代入演算子(=)の左辺の変数の数が、右辺の値の数と一致していなければなりません。 値を区切るには、カンマ,を使います:

>>> a, b = 1, 2
>>> a
1

多重代入で、与えられた値に対して変数の数が合わないと、ValueErrorが送出されます:

>>> x, y, z = 1, 2

ValueError: too many values to unpack (expected 3, got 2)

多重代入は1つのデータ型に限定されません:

>>> x, y, z = 1, "Hello", True
>>> x
1

>>> y
'Hello'

>>> z
True

多重代入を使ってlistsの要素を入れ替えられます。 このやり方はソートアルゴリズムでよく使われます。 例えば:

>>> numbers = [1, 2]
>>> numbers[0], numbers[1] = numbers[1], numbers[0]
>>> numbers
[2, 1]

tuplesはイミュータブルなので、tupleの要素を入れ替えることはできません。

アンパック

Note

以下の例ではlistsを使っていますが、同じ考え方はtuplesにも当てはまります。

Pythonでは、list/tuple/dictの要素をアンパックして、個別の変数に代入できます。 値はlists/tuplesの中で特定の順序で並んでいるため、同じ順序で変数にアンパックされます:

>>> fruits = ["apple", "banana", "cherry"]
>>> x, y, z = fruits
>>> x
"apple"

不要な値がある場合は、_を使ってそれを示せます:

>>> fruits = ["apple", "banana", "cherry"]
>>> _, _, z = fruits
>>> z
"cherry"

深いアンパック

listやtupleの内側にあるlist/tupleから値をアンパックして代入する操作(入れ子の配列/タプルとも呼ばれます)は、浅いアンパックと同じように動きますが、値の文脈や位置を明確にするための追加の指定が必要になることがよくあります:

>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>>> [[a, b], [c, d]] = fruits_vegetables
>>> a
"apple"

>>> d
"potato"

入れ子のlist/tupleの一部だけを深くアンパックすることもできます:

>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>>> [a, [c, d]] = fruits_vegetables
>>> a
["apple", "banana"]

>>> c
"carrot"

アンパックで変数の位置が正しくなかったり、値の数が合わなかったりすると、ValueErrorが発生します:

>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>>> [[a, b], [d]] = fruits_vegetables

ValueError: too many values to unpack (expected 1)

*を使った配列/タプルのアンパック

list/tupleをアンパックするときは、*演算子を使って「残りの」値をまとめて取り出せます。 これは、list/tupleをスライスするより(状況によっては読みにくくなります)明確です。 例えば、最初の要素を取り出し、残りの値を最初の要素を含まない新しいlistにパックできます:

>>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
>>> x, *last = fruits
>>> x
"apple"

>>> last
["banana", "cherry", "orange", "kiwi", "melon", "mango"]

listの先頭と末尾の値を取り出しつつ、真ん中の値をすべてまとめることもできます:

>>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
>>> x, *middle, y, z = fruits
>>> y
"melon"

>>> middle
["banana", "cherry", "orange", "kiwi"]

深いアンパックでも*を使えます:

>>> fruits_vegetables = [["apple", "banana", "melon"], ["carrot", "potato", "tomato"]]
>>> [[a, *rest], b] = fruits_vegetables
>>> a
"apple"

>>> rest
["banana", "melon"]

辞書のアンパック

辞書のアンパックは、list/tupleのアンパックとは少し違います。 辞書の反復は、デフォルトではキーに対して行われます。 そのため、dictをアンパックするときは、キーだけをアンパックでき、値はアンパックできません:

>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> x, y, z = fruits_inventory
>>> x
"apple"

値をアンパックしたい場合は、<dict>.values()メソッドを使います:

>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> x, y, z = fruits_inventory.values()
>>> x
6

キーと値の両方が必要な場合は、<dict>.items()メソッドを使います。 <dict>.items()は、キーと値のペアを含むイテラブルなビューを生成します。 これはtupleにアンパックできます:

>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> x, y, z = fruits_inventory.items()
>>> x
("apple", 6)

パック

パックとは、複数の値を1つのlistにまとめて変数に代入できる機能です。 これは、値を_アンパック_して変更を加え、その結果を再び変数に_パック_したいときに便利です。 また、2つ以上のlists/tuples/dictsをマージすることもできます。

*を使った配列/タプルのパック

list/tupleのパックは、*演算子を使って行えます。 これにより、すべての値が1つのlist/tupleにパックされます。

>>> fruits = ("apple", "banana", "cherry")
>>> more_fruits = ["orange", "kiwi", "melon", "mango"]

# fruits and more_fruits are unpacked and then their elements are packed into combined_fruits
>>> combined_fruits = *fruits, *more_fruits

# If there is no * on to the left of the "=" the result is a tuple
>>> combined_fruits
("apple", "banana", "cherry", "orange", "kiwi", "melon", "mango")

# If the * operator is used on the left side of "=" the result is a list.
# Note the trailing comma. 
>>> *combined_fruits_too, = *fruits, *more_fruits
>>> combined_fruits_too
['apple', 'banana', 'cherry', 'orange', 'kiwi', 'melon', 'mango']

# A list literal can be used instead, but might not be as readable.
>>> [*combined_fruits_too] = *fruits, *more_fruits
>>> combined_fruits_too
['apple', 'banana', 'cherry', 'orange', 'kiwi', 'melon', 'mango']

*と**の使い方の詳細については、PEP 3132とPEP 448を参照してください。

**を使った辞書のパック

辞書のパックは、**演算子を使って行います。 これにより、ある辞書のキーと値のペアがすべて別の辞書にパックされるか、2つの辞書が1つにまとめられます。

>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> more_fruits_inventory = {"orange": 4, "kiwi": 1, "melon": 2, "mango": 3}

# fruits_inventory and more_fruits_inventory are unpacked into key-values pairs and combined.
>>> combined_fruits_inventory = {**fruits_inventory, **more_fruits_inventory}

# then the pairs are packed into combined_fruits_inventory
>>> combined_fruits_inventory
{"apple": 6, "banana": 2, "cherry": 3, "orange": 4, "kiwi": 1, "melon": 2, "mango": 3}

関数での*と**の使い方

関数の仮引数を使ったパック

任意の数の引数を受け取る関数を作るときは、関数定義で*argsまたは**kwargsを使えます。 *argsは任意の数の位置引数(キーワードでない引数)をパックするのに使われ、 **kwargsは任意の数のキーワード引数をパックするのに使われます。

*argsの使い方:

# This function is defined to take any number of positional arguments

>>> def my_function(*args):
...   print(args)

# Arguments given to the function are packed into a tuple

>>> my_function(1, 2, 3)
(1, 2, 3)

>>> my_function("Hello")
("Hello")

>>> my_function(1, 2, 3, "Hello", "Mars")
(1, 2, 3, "Hello", "Mars")

**kwargsの使い方:

# This function is defined to take any number of keyword arguments

>>> def my_function(**kwargs):
...   print(kwargs)

# Arguments given to the function are packed into a dictionary

>>> my_function(a=1, b=2, c=3)
{"a": 1, "b": 2, "c": 3}

*argsと**kwargsは組み合わせて使うこともできます:

>>> def my_function(*args, **kwargs):
...   print(sum(args))
...   for key, value in kwargs.items():
...       print(str(key) + " = " + str(value))

>>> my_function(1, 2, 3, a=1, b=2, c=3)
6
a = 1
b = 2
c = 3

*argsの前に仮引数を書いて、特定の位置引数を許可することもできます。 その場合、個々のキーワード引数は**kwargsより前に書かなければなりません。

Caution

引数は次のように構成する必要があります:

def my_function(<positional_args>, *args, <key-word_args>, **kwargs)

この順序に従わないと、エラーになります。

>>> def my_function(a, b, *args):
...   print(a)
...   print(b)
...   print(args)

>>> my_function(1, 2, 3, 4, 5)
1
2
(3, 4, 5)

引数を間違った順序で書くと、エラーになります:

>>> def my_function(*args, a, b):
...   print(args)

>>>my_function(1, 2, 3, 4, 5)
Traceback (most recent call last):
  File "c:\something.py", line 3, in <module>
    my_function(1, 2, 3, 4, 5)
TypeError: my_function() missing 2 required keyword-only arguments: 'a' and 'b'

関数呼び出しへのアンパック

*を使って、引数のlist/tupleを関数呼び出しにアンパックできます。 これは、iterableを受け取らない関数にとても便利です:

>>> def my_function(a, b, c):
...   print(c)
...   print(b)
...   print(a)

numbers = [1, 2, 3]
>>> my_function(*numbers)
3
2
1

zip()組み込み関数と組み合わせて*アンパックを使うのも、もう1つのよくある使用例です。 zip()関数は複数のイテラブルを受け取り、各iterableの値をまとめたtuplesのlistを返します:

>>> values = (['x', 'y', 'z'], [1, 2, 3], [True, False, True])
>>> a, *rest = zip(*values)
>>> rest
[('y', 2, False), ('z', 3, True)]
GitHubで編集 リンクは新しいウィンドウまたはタブで開きます

アンパックと多重代入を学習する

練習はロックされています

アンパックと多重代入を練習するには、あと3個の演習のロックを解除してください