Pythonでアンパックする。並列代入の先にあるもの

Python のアンパッキングとは、1つの代入文の中で、変数のタプル(または リスト )に反復可能な値を代入する操作のことを指します。

補完的に、反復可能なアンパッキング演算子 * を使って、いくつかの値を1つの変数に集めるとき、パッキングという用語を使うことができます。

歴史的に、Pythonの開発者はこのような操作を一般的にタプルアンパッキングと呼んできました。

しかし、このPythonの機能は非常に便利で人気があることがわかったので、すべての種類の反復処理に一般化されました。

現在では、より現代的で正確な用語として、反復子解凍(iterable unpacking)があります。

このチュートリアルでは、反復子解凍とは何か、そして私たちのコードをより読みやすく、保守的に、そしてPythonicにするためにこのPythonの機能をどのように利用できるかを学びます。

さらに、代入操作、forループ、関数定義、関数呼び出しのコンテキストで、反復可能なアンパッキング機能をどのように使うか、いくつかの実用的な例も取り上げる予定です。

Pythonのパッキングとアンパッキング

Python では、代入操作の左辺に変数の タプル (または リスト) を配置することができます。

タプルの各変数は、右側のイテラブルから1つの値 (または*` 演算子を使えば、それ以上の値) を受け取ることができます。

歴史的な理由から、Pythonの開発者はこれをタプルのアンパッキングと呼んでいました。

しかし、この機能はすべての種類の反復記号に一般化されているので、より正確には反復記号の解凍と呼び、このチュートリアルでもそう呼ぶことにします。

アンパッキング操作はPythonの開発者の間で非常に人気がありますが、それはコードをより読みやすく、エレガントにすることができるからです。

Pythonのアンパッキングを詳しく見て、この機能がどのように私たちのコードを改善できるかを見てみましょう。

タプルの解凍

Python では、代入演算子 (=) の左辺に変数の タプル を置き、右辺に値の タプル を置くことができます。

右側の値は、タプル内の位置に従って、左側の変数に自動的に代入されます。

これは、Pythonでは一般的にタプルアンパッキングと呼ばれています。

次の例を見てください。

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


代入演算子の両側にタプルを置いたとき、タプルのアンパッキング操作が行われます。

右側の値は、それぞれの タプル における相対的な位置に応じて、左側の変数に代入されます。

上の例でわかるように、 a には 1 が、 b には 2 が、 c には 3 が入ることになります。

タプルオブジェクトを作成するために、区切り文字として一対の括弧()` を使用する必要はない。

これはタプルを展開する際にも有効であり、以下の構文は等価である。

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


これらのバリエーションはすべてPythonの有効な構文なので、状況に応じてどれでも使うことができます。

Pythonで解凍する場合、最後の構文がより一般的に使用されるのは間違いありません。

タプルアンパッキングを使って値を変数に展開するとき、左側の tuple にある変数の数と右側の tuple にある値の数は正確に一致しなければなりません。

そうでない場合は、ValueError が発生します。

例えば、以下のコードでは、左辺に2つの変数、右辺に3つの値を使用しています。

これは、解凍する値が多すぎることを伝える ValueError を発生させます。

>>> a, b = 1, 2, 3
Traceback (most recent call last):
  ...
ValueError: too many values to unpack (expected 2)


注意: 唯一の例外は、後で説明するように、 * 演算子を使って複数の値を1つの変数に格納する場合です。

一方、値よりも多くの変数を使用した場合は ValueError が発生しますが、この場合は「解凍する値が足りない」というメッセージが表示されます。

>>> a, b, c = 1, 2
Traceback (most recent call last):
  ...
ValueError: not enough values to unpack (expected 3, got 2)


もしタプルのアンパッキング操作で、異なる数の変数と値を使うと、 ValueError が発生します。

これは、Pythonがどの値がどの変数に入るかを明確に知る必要があり、それに応じて代入を行うことができるためです。

イテラブルの解凍

タプルのアンパッキング機能は Python 開発者の間でとても人気があり、この構文は任意のイテラブルオブジェクトで動作するように拡張されました。

唯一の要件は、反復子オブジェクトが受け取る tuple (または list) の変数ごとに正確に1つの項目を生成することです。

Pythonで反復処理可能なオブジェクトの解凍がどのように行われるか、次の例を見てください。

>>> # Unpacking strings
>>> a, b, c = '123'
>>> a
'1'
>>> b
'2'
>>> c
'3'
>>> # Unpacking lists
>>> a, b, c = [1, 2, 3]
>>> a
1
>>> b
2
>>> c
3
>>> # Unpacking generators
>>> gen = (i ** 2 for i in range(3))
>>> a, b, c = gen
>>> a
0
>>> b
1
>>> c
4
>>> # Unpacking dictionaries (keys, values, and items)
>>> my_dict = {'one': 1, 'two':2, 'three': 3}
>>> a, b, c = my_dict  # Unpack keys
>>> a
'one'
>>> b
'two'
>>> c
'three'
>>> a, b, c = my_dict.values()  # Unpack values
>>> a
1
>>> b
2
>>> c
3
>>> a, b, c = my_dict.items()  # Unpacking key-value pairs
>>> a
('one', 1)
>>> b
('two', 2)
>>> c
('three', 3)


Pythonのアンパッキングでは、代入演算子の右辺に任意の反復記号を使うことができます。

左辺には タプルリスト といった変数が入ります。

次の例では、代入文の右辺に tuple を使っています。

>>> [a, b, c] = 1, 2, 3
>>> a
1
>>> b
2
>>> c
3


これは、 range() イテレータを使用しても同じように動作します。

>>> x, y, z = range(3)
>>> x
0
>>> y
1
>>> z
2


これは有効なPythonの構文ですが、実際のコードではあまり使われず、初心者のPython開発者には少しわかりにくいかもしれません。

最後に、解凍の操作で set オブジェクトを使うこともできます。

しかし、セットは順序のないコレクションなので、代入の順序はある種支離滅裂で、微妙なバグにつながる可能性があります。

次の例を見てください。

>>> a, b, c = {'a', 'b', 'c'}
>>> a
'c'
>>> b
'b'
>>> c
'a'


もし、解凍操作にセットを使うと、最終的な割り当ての順序は、私たちが望むもの、期待するものとは全く異なるものになります。

ですから、割り当ての順序がコードにとって重要でない限り、解凍操作にセットを使うのは避けた方がよいでしょう。

この文脈では、* 演算子はタプル(または反復可能)解凍演算子として知られています。

これは、解凍機能を拡張して、複数の値を 1 つの変数に集めたり、パックしたりできるようにします。

次の例では、 * 演算子を用いて、 タプル 型の値を 1 つの変数にまとめています。

>>> *a, = 1, 2
>>> a
[1, 2]


このコードが動作するためには、代入の左辺が タプル (または リスト) である必要があります。

そのため、末尾にカンマを使用します。

この タプル には必要なだけの変数を含めることができます。

しかし、スタード表現は1つしか入れることができません。

上のコードにある *a のように、正規のPython識別子と一緒にアンパッキング演算子 * を使用すると、星型式を作成することができます。

左側の tuple にある残りの変数は必須変数と呼ばれ、具体的な値で埋めなければならないので、そうしないとエラーになります。

実際にどのように動作するかというと、以下のようになります。

末尾の値を b に詰める。

>>> a, *b = 1, 2, 3
>>> a
1
>>> b
[2, 3]


開始値を a に詰める。

>>> *a, b = 1, 2, 3
>>> a
[1, 2]
>>> b
3


bcは必須なので、a` に1つだけ値を詰め込む。

>>> *a, b, c = 1, 2, 3
>>> a
[1]
>>> b
2
>>> c
3


b,c,dが必須なので、aには値を入れません (aのデフォルトは[]`):

>>> *a, b, c, d = 1, 2, 3
>>> a
[]
>>> b
1
>>> c
2
>>> d
3


必須変数 (e) に値がないので、エラーになる。

>>> *a, b, c, d, e = 1, 2, 3
 ...
ValueError: not enough values to unpack (expected at least 4, got 3)


リスト()関数を使わずに、ジェネレータの要素を1つの変数に集めたい場合には、演算子を使って変数に値を詰め込むと便利です。

以下の例では、` 演算子を使って、ジェネレータ式の要素とレンジオブジェクトを個別の変数にパックしています。

>>> gen = (2 ** x for x in range(10))
>>> gen
<generator <genexpr="" object="" at 0x7f44613ebcf0&gt;
&gt;&gt;&gt; *g, = gen
&gt;&gt;&gt; g
[1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
&gt;&gt;&gt; ran = range(10)
&gt;&gt;&gt; *r, = ran
&gt;&gt;&gt; r
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]


これらの例では、 * 演算子は genran の要素をそれぞれ gr にパックしています。

このシンタックスでは、 range オブジェクト、ジェネレータ式、ジェネレータ関数から list を生成するために list() を呼び出す必要がありません。

また、代入の左側にある変数に末尾のカンマを追加しないと、解凍演算子 * を使用して複数の値を1つの変数にまとめることができないことに注意してください。

ですから、次のようなコードはうまくいきません。

&gt;&gt;&gt; *r = range(10)
  File "<input/", line 1
SyntaxError: starred assignment target must be in a list or tuple


もし、 * 演算子を使って複数の値を一つの変数に詰め込もうとするなら、シングルトンの tuple シンタックスを使う必要があります。

例えば、上記の例を動作させるには、*r, = range(10) のように変数 r の後にカンマを追加すればよいのです。

梱包と開梱を実際に使ってみる

PackingとUnpackingの操作は、実際にはかなり有用です。

それらはあなたのコードを明確に、読みやすく、そしてPythonicにすることができます。

Pythonでpackingとunpackの一般的な使用例をいくつか見てみましょう。

並列に割り当てる

Pythonでunpackの最も一般的な使用例の1つは、並列代入と呼ぶことができるものです。

並列代入では、イテラブルの値をタプル変数(またはリスト)に代入することを、1つのエレガントなステートメントで行うことができます。

例えば、会社の従業員に関するデータベースがあり、リストの各項目を説明変数に代入する必要があるとします。

Pythonのiterable unpackingがどのように動作するかを無視すると、次のようなコードを書くことになります。

&gt;&gt;&gt; employee = ["John Doe", "40", "Software Engineer"]
&gt;&gt;&gt; name = employee[0]
&gt;&gt;&gt; age = employee[1]
&gt;&gt;&gt; job = employee[2]
&gt;&gt;&gt; name
'John Doe'
&gt;&gt;&gt; age
'40'
&gt;&gt;&gt; job
'Software Engineer'


このコードは動作しますが、インデックスの扱いが不格好で、タイプしづらく、混乱を招く可能性があります。

よりきれいで、より読みやすく、Python的な解決策は次のようにコード化できます。

&gt;&gt;&gt; name, age, job = ["John Doe", "40", "Software Engineer"]
&gt;&gt;&gt; name
'John Doe'
&gt;&gt;&gt; age
40
&gt;&gt;&gt; job
'Software Engineer'


Pythonのunpackを使うと、前の例の問題を、単一の、簡単で、エレガントな文で解決することができます。

この小さな変更は、新しい開発者にとって読みやすく、理解しやすいコードになるでしょう。

変数間の値の入れ替え

Python のアンパッキングのもう一つのエレガントな使い方は、一時変数や補助変数を使わずに変数間の値を交換することです。

例えば、2つの変数 ab の値を交換する必要があるとします。

これを行うには、伝統的な方法にこだわって、以下のようにスワップされる値を格納する一時変数を使用します。

&gt;&gt;&gt; a = 100
&gt;&gt;&gt; b = 200
&gt;&gt;&gt; temp = a
&gt;&gt;&gt; a = b
&gt;&gt;&gt; b = temp
&gt;&gt;&gt; a
200
&gt;&gt;&gt; b
100


この手続きは3つのステップと新しい一時変数を必要とします。

Pythonのunpackを使えば、同じ結果を1つの簡潔なステップで達成できます。

&gt;&gt;&gt; a = 100
&gt;&gt;&gt; b = 200
&gt;&gt;&gt; a, b = b, a
&gt;&gt;&gt; a
200
&gt;&gt;&gt; b
100


a, b = b, aというステートメントでは、1行のコードでabに、ba` に再代入しているのです。

これは、より可読性が高く、分かりやすい方法です。

また、このテクニックでは、新しいテンポラリ変数を用意する必要がないことに注意してください。

Collecting Multiple Values With *.

アルゴリズムを使って作業しているとき、反復可能な値やシーケンスの値をチャンクに分割してさらに処理する必要がある場合があります。

次の例では、list とスライス操作を使用して、この処理を行います。

&gt;&gt;&gt; seq = [1, 2, 3, 4]
&gt;&gt;&gt; first, body, last = seq[0], seq[1:3], seq[-1]
&gt;&gt;&gt; first, body, last
(1, [2, 3], 4)
&gt;&gt;&gt; first
1
&gt;&gt;&gt; body
[2, 3]
&gt;&gt;&gt; last
4


このコードは期待通りに動作しますが、インデックスやスライスを扱うのは少し面倒で、読みづらく、初心者には分かりにくいかもしれません。

また、コードが硬直化し、メンテナンスが困難になるという欠点もあります。

このような場合、イテラブルのアンパッキング演算子である * と、複数の値を1つの変数にパックする機能は、素晴らしいツールになりえます。

上記のコードのリファクタリングをご覧ください。

&gt;&gt;&gt; seq = [1, 2, 3, 4]
&gt;&gt;&gt; first, *body, last = seq
&gt;&gt;&gt; first, body, last
(1, [2, 3], 4)
&gt;&gt;&gt; first
1
&gt;&gt;&gt; body
[2, 3]
&gt;&gt;&gt; last
4


first, body, last = seqという行が、このマジックです。

反復可能なアンパッキング演算子であるは、seqの真ん中の要素をbodyに集めているのです。

これによって、コードの可読性、保守性、柔軟性が向上する。

なぜより柔軟なのか、と思われるかもしれません。

例えば、seqの長さが途中で変わった場合でも、真ん中の要素をbody` に集めなければならないとします。

この場合、Pythonのunpackを使っているので、私たちのコードが動作するために変更する必要はありません。

この例を見てください。

&gt;&gt;&gt; seq = [1, 2, 3, 4, 5, 6]
&gt;&gt;&gt; first, *body, last = seq
&gt;&gt;&gt; first, body, last
(1, [2, 3, 4, 5], 6)


もしPythonで反復子解凍の代わりにシーケンススライスを使用していたなら、新しい値を正しくキャッチするためにインデックスとスライスを更新する必要があります。

演算子 * を使っていくつかの値を1つの変数にまとめることは、Pythonがそれぞれの変数に割り当てる要素を明確に決定できるのであれば、様々な構成で適用することができます。

次の例を見てください。

&gt;&gt;&gt; *head, a, b = range(5)
&gt;&gt;&gt; head, a, b
([0, 1, 2], 3, 4)
&gt;&gt;&gt; a, *body, b = range(5)
&gt;&gt;&gt; a, body, b
(0, [1, 2, 3], 4)
&gt;&gt;&gt; a, b, *tail = range(5)
&gt;&gt;&gt; a, b, tail
(0, 1, [2, 3, 4])


変数の タプル (または リスト) の中で * 演算子を動かして、必要な値を集めることができます。

唯一の条件は、Pythonがどの変数に各値を代入するかを決定できることです。

ここで重要なのは、代入の際に複数の式を使ってはいけないということです。

もしそうしてしまうと、次のような SyntaxError が発生してしまうでしょう。

&gt;&gt;&gt; *a, *b = range(5)
  File "<input/", line 1
SyntaxError: two starred expressions in assignment


もし、代入式の中で2つ以上の * を使用すると、2つの星付き式が見つかったことを伝える SyntaxError が発生します。

これは、Pythonが各変数にどのような値を代入したいのかを一義的に判断できないため、このような表現になっています。

Dropping Unneeded Values With *.

もう一つの一般的な使用例として、ダミーの変数名と一緒に使って、無駄な値や不要な値を削除することができます。

次の例を見てください。

&gt;&gt;&gt; a, b, *_ = 1, 2, 0, 0, 0, 0
&gt;&gt;&gt; a
1
&gt;&gt;&gt; b
2
&gt;&gt;&gt; _
[0, 0, 0, 0]


この使用例についてより深く理解するために、使用しているPythonのバージョンを決定する必要があるスクリプトを開発しているとします。

これを行うには、 sys.version_info 属性を使用します。

この属性は、バージョン番号の5つの要素、 majorminormicroreleasevelserial を含むタプルを返します。

しかし、スクリプトを動作させるためには、 major, minor, micro が必要なだけなので、残りは削除しても構いません。

以下はその例です。

&gt;&gt;&gt; import sys
&gt;&gt;&gt; sys.version_info
sys.version_info(major=3, minor=8, micro=1, releaselevel='final', serial=0)
&gt;&gt;&gt; mayor, minor, micro, *_ = sys.version_info
&gt;&gt;&gt; mayor, minor, micro
(3, 8, 1)


これで、必要な情報を持つ新しい変数が3つできました。

残りの情報はダミー変数 _ に格納され、プログラムでは無視することができます。

このダミー変数に格納された情報は、プログラムでは無視することができます。

このようにすることで、初心者の開発者に _ に格納された情報を使いたくない (使う必要がない) ということを明確にすることができます。

注意: デフォルトでは、アンダースコア文字 _ は Python インタープリタによって、対話型セッションで実行したステートメントの結果の値を格納するために使用されます。

そのため、この文脈では、ダミー変数を識別するためにこの文字を使用することは曖昧になる可能性があります。

関数でタプルを返す

Python の関数はカンマで区切られた複数の値を返すことができます。

括弧を使わずに tuple オブジェクトを定義できるので、この種の操作は値の tuple を返すと解釈することができます。

もし、複数の値を返す関数を記述すれば、返された値に対して反復可能なパッキングとアンパッキングの操作を行うことができます。

次の例では、与えられた数値の2乗と3乗を計算する関数を定義しています。

&gt;&gt;&gt; def powers(number):
...     return number, number ** 2, number ** 3
...
&gt;&gt;&gt; # Packing returned values in a tuple
&gt;&gt;&gt; result = powers(2)
&gt;&gt;&gt; result
(2, 4, 8)
&gt;&gt;&gt; # Unpacking returned values to multiple variables
&gt;&gt;&gt; number, square, cube = powers(2)
&gt;&gt;&gt; number
2
&gt;&gt;&gt; square
4
&gt;&gt;&gt; cube
8
&gt;&gt;&gt; *_, cube = powers(2)
&gt;&gt;&gt; cube
8


カンマで区切られた値を返す関数を定義すれば、これらの値に対して任意の詰め込み/解凍操作を行うことができます。

##Merging Iterables With * Operator

unpacking 演算子 * のもう一つの興味深い使用例は、複数の反復記号を最終的なシーケンスにマージする機能です。

この機能は、リスト、タプル、およびセットに対して機能します。

次の例を見てください。

&gt;&gt;&gt; my_tuple = (1, 2, 3)
&gt;&gt;&gt; (0, *my_tuple, 4)
(0, 1, 2, 3, 4)
&gt;&gt;&gt; my_list = [1, 2, 3]
&gt;&gt;&gt; [0, *my_list, 4]
[0, 1, 2, 3, 4]
&gt;&gt;&gt; my_set = {1, 2, 3}
&gt;&gt;&gt; {0, *my_set, 4}
{0, 1, 2, 3, 4}
&gt;&gt;&gt; [*my_set, *my_list, *my_tuple, *range(1, 4)]
[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]
&gt;&gt;&gt; my_str = "123"
&gt;&gt;&gt; [*my_set, *my_list, *my_tuple, *range(1, 4), *my_str]
[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, '1', '2', '3']


シーケンスを定義する際に、反復子解凍演算子 * を使用すると、部分シーケンス(または反復子)の要素を解凍して最終的なシーケンスにすることができます。

これにより、append()insert() などのメソッドを呼び出すことなく、既存のシーケンスからその場でシーケンスを作成することができるようになります。

最後の2つの例では、イテレータブルを連結する方法としても、より可読性が高く効率的な方法であることを示しました。

list(my_set) + my_list + list(my_tuple) + list(range(1, 4)) と書くかわりに + と書く代わりに、[*my_set, *my_list, *my_tuple, *range(1, 4), *my_str] と書くだけです。

Unpacking Dictionaries With *** Operator

Pythonの解凍の文脈では、**演算子は辞書解凍演算子と呼ばれます。

この演算子の使用は PEP 448 で拡張されました。

現在では、関数呼び出し、内包やジェネレータ式、表示で使用することができます。

辞書展開演算子の基本的な使用例としては、1つの式で複数の辞書を1つの最終的な辞書にマージすることが挙げられます。

その方法を見てみましょう。

&gt;&gt;&gt; numbers = {"one": 1, "two": 2, "three": 3}
&gt;&gt;&gt; letters = {"a": "A", "b": "B", "c": "C"}
&gt;&gt;&gt; combination = {**numbers, **letters}
&gt;&gt;&gt; combination
{'one': 1, 'two': 2, 'three': 3, 'a': 'A', 'b': 'B', 'c': 'C'}


辞書表示の内部で辞書展開演算子を使用する場合、上記のコードで行ったように、辞書を展開し、それらを結合して、元の辞書のキーと値のペアを含む最終的な辞書を作成することができます。

注意すべき重要な点は、結合しようとする辞書に繰り返しキーや共通キーがある場合、一番右の辞書の値が一番左の辞書の値を上書きすることです。

以下はその例である。

&gt;&gt;&gt; letters = {"a": "A", "b": "B", "c": "C"}
&gt;&gt;&gt; vowels = {"a": "a", "e": "e", "i": "i", "o": "o", "u": "u"}
&gt;&gt;&gt; {**letters, **vowels}
{'a': 'a', 'b': 'B', 'c': 'C', 'e': 'e', 'i': 'i', 'o': 'o', 'u': 'u'}


aのキーは両方の辞書に存在するので、優先される値は一番右の辞書であるvowelsから取得されます。

これは、Pythonが左から右へとキーと値のペアを追加し始めるために起こります。

もしその過程で、Pythonがすでに終了しているキーを見つけたら、インタープリタはそのキーを新しい値で更新します。

上の例でa` キーの値が小文字になっているのはこのためです。

For-Loopでのアンパッキング

イテラブルのアンパッキングは for ループのコンテキストでも使うことができます。

forループを実行すると、ループは反復ごとにターゲット変数にイテラブルの項目を1つずつ代入していきます。

もし、代入される項目がイテラブルであれば、ターゲット変数のタプルを使用することができます。

ループは手元の反復処理可能な変数を、ターゲット変数のタプル` に展開する。

例として、ある会社の売上に関するデータを含む以下のようなファイルがあるとします。

| — | — | — |
| 鉛筆|0.25|1500|本
| ノートブック|1.30|550|(税込み
| 消しゴム|0.75|1000|。

このテーブルから、2つの要素を持つタプルの リスト を作成することができます。

それぞれの tuple には、商品名、価格、販売個数が含まれます。

この情報を使って、各製品の所得を計算したいと思います。

これを行うには、次のような for ループを使用します。

&gt;&gt;&gt; sales = [("Pencil", 0.22, 1500), ("Notebook", 1.30, 550), ("Eraser", 0.75, 1000)]
&gt;&gt;&gt; for item in sales:
...     print(f"Income for {item[0]} is: {item[1] * item[2]}")
...
Income for Pencil is: 330.0
Income for Notebook is: 715.0
Income for Eraser is: 750.0


このコードは期待通りに動作します。

しかし、各 tuple の個々の要素にアクセスするためにインデックスを使用しています。

これは、新参の開発者にとっては読みにくく、理解しにくいものです。

Pythonのunpackを使った別の実装を見てみましょう。

&gt;&gt;&gt; for product, price, sold_units in sales:
...     print(f"Income for {product} is: {price * sold_units}")
...
Income for Pencil is: 330.0
Income for Notebook is: 715.0
Income for Eraser is: 750.0


ここでは、forループの中で反復子解凍を使っています。

これは、各tupleの要素を識別するために説明的な名前を使用しているので、コードをより読みやすく、保守しやすくしています。

この小さな変更により、新参の開発者でもすぐにコードの背後にあるロジックを理解することができるようになります。

また、forループの中で * 演算子を使用すると、複数の項目を1つのターゲット変数にまとめることができます。

&gt;&gt;&gt; for first, *rest in [(1, 2, 3), (4, 5, 6, 7)]:
...     print("First:", first)
...     print("Rest:", rest)
...
First: 1
Rest: [2, 3]
First: 4
Rest: [5, 6, 7]


この for ループでは、各シーケンスの最初の要素を first でキャッチしています。

次に、* 演算子が list の値をターゲット変数 rest に取り込みます。

最後に、ターゲット変数の構造はイテラブルの構造と一致している必要があります。

そうでなければ、エラーになります。

次の例を見てみましょう。

&gt;&gt;&gt; data = [((1, 2), 2), ((2, 3), 3)]
&gt;&gt;&gt; for (a, b), c in data:
...     print(a, b, c)
...
1 2 2
2 3 3
&gt;&gt;&gt; for a, b, c in data:
...     print(a, b, c)
...
Traceback (most recent call last):
  ...
ValueError: not enough values to unpack (expected 3, got 2)


最初のループでは、ターゲット変数の構造 (a, b), c は、反復処理可能なアイテムの構造 ((1, 2), 2) と一致します。

この場合、ループは期待通りに動作します。

一方、2番目のループでは、ターゲット変数の構造がイテラブルのアイテムの構造と一致しないため、ループは失敗し ValueError が発生します。

機能における梱包と開梱について

Pythonのパッキングとアンパッキングの機能は、関数を定義して呼び出すときにも使うことができます。

これはPythonのパッキングとアンパッキングの非常に便利で一般的な使用例です。

このセクションでは、Pythonの関数において、関数定義または関数呼び出しのどちらかでパッキングとアンパッキングを使用する方法の基本を説明します。

注:これらのトピックに関するより深い洞察と詳細な資料については、Pythonにおける *args**kwargs を使った可変長引数をチェックしてください。

Defining Functions With * and * *.

Pythonの関数のシグネチャには、***という演算子を使用することができます。

これにより、可変数の位置引数(*)、可変数のキーワード引数、またはその両方で関数を呼び出すことができるようになります。

次のような関数を考えてみましょう。

&gt;&gt;&gt; def func(required, *args, **kwargs):
...     print(required)
...     print(args)
...     print(kwargs)
...
&gt;&gt;&gt; func("Welcome to...", 1, 2, 3, site='StackAbuse.com')
Welcome to...
(1, 2, 3)
{'site': 'StackAbuse.com'}


上の関数は少なくとも1つの required と呼ばれる引数を必要とします。

この関数は、可変数の位置引数やキーワード引数を受け付けることができます。

この場合、 * 演算子は追加の位置引数を args というタプルに、 ** 演算子は追加のキーワード引数を kwargs という辞書に集めたり、まとめたりすることになります。

argskwargsはどちらもオプションで、デフォルトではそれぞれ(){}` になります。

argskwargsという名前は Python コミュニティで広く使われているが、これらのテクニックを動作させるための必須条件ではない。

この構文では、または*` の後に有効な識別子が必要なだけです。

ですから、もしこれらの引数に意味のある名前を付けることができるのであれば、そうしてください。

そうすれば、あなたのコードの可読性は確実に向上するでしょう。

############################## * と **** を使った関数の呼び出し

関数を呼び出すとき、*** 演算子を使用して、引数のコレクションをそれぞれ別の位置引数またはキーワード引数にアンパックすることもできます。

これは、関数のシグネチャで *** を使用することの逆です。

シグネチャでは、これらの演算子は、可変数の引数を1つの識別子に集めたり、まとめたりすることを意味します。

呼び出し側では、イテラブルをいくつかの引数に分解することを意味します。

以下は、その基本的な例です。

&gt;&gt;&gt; def func(welcome, to, site):
...     print(welcome, to, site)
...
&gt;&gt;&gt; func(*["Welcome", "to"], **{"site": 'StackAbuse.com'})
Welcome to StackAbuse.com


ここで、* 演算子は ["Welcome", "to"] のようなシーケンスを位置引数に展開します。

同様に、 ** 演算子は辞書を展開し、展開された辞書のキーに一致する名前を持つ引数にします。

また、このテクニックと前のセクションで説明したテクニックを組み合わせることで、非常に柔軟な関数を書くことができます。

以下はその例です。

&gt;&gt;&gt; def func(required, *args, **kwargs):
...     print(required)
...     print(args)
...     print(kwargs)
...
&gt;&gt;&gt; func("Welcome to...", *(1, 2, 3), **{"site": 'StackAbuse.com'})
Welcome to...
(1, 2, 3)
{'site': 'StackAbuse.com'}


Pythonの関数を定義したり呼び出したりするときに *** 演算子を使用することで、関数に特別な機能を与え、より柔軟で強力な関数にすることができます。

結論

イテラブルのアンパッキングは、Pythonのかなり便利で人気のある機能であることがわかりました。

この機能により、イテラブルをいくつかの変数にアンパックすることができます。

一方、パッキングは、アンパッキング演算子 * を使って、複数の値を1つの変数に取り込むことです。

このチュートリアルでは、Pythonで反復可能なアンパッキングを使用して、より読みやすく、保守的で、Pythonicなコードを書く方法を学びました。

この知識により、Pythonの反復子解凍を使用して、並列代入や変数間の値のスワップなどの一般的な問題を解決できるようになりました。

また、forループ、関数呼び出し、関数定義のような他の構造でこのPythonの機能を使用することができるようになりました。

タイトルとURLをコピーしました