Pythonでアンパッキング演算子(*、**)を使用する方法は?

公開: 2021-07-02

Pythonは最も使用されているプログラミング言語です。 今日は、Pythonでアンパックする、そのコア機能の1つ(ただし、無視されることが多い)の使用方法を学習します。

あなたはおそらく他のコードで*と**を見たことがあるか、実際にそれらの目的が何であるかを知らずにそれらを使用したことさえあります。 アンパックの概念と、それを使用してより多くのPythonicコードを作成する方法について説明します。

このチュートリアルを読んでいるときに役立つ概念のリストを次に示します。

  • 反復可能な:セット、リスト、タプル、および辞書のようなforループで反復することができる任意の順序、
  • 呼び出し可能:二重括弧()を使用して呼び出すことができるPythonオブジェクト(例: myfunction())
  • シェル: Pythonコードを実行できるインタラクティブなランタイム環境。 ターミナルで「python」を実行することで呼び出すことができます
  • 変数:オブジェクトを格納し、予約されたメモリ位置を持つ記号名。

最も頻繁な混乱から始めましょう。Pythonのアステリスティックは算術演算子でもあります。 1つのアスタリスク(*)は乗算に使用され、そのうちの2つ(**)はべき乗を示します。

Pythonシェルを開いて次のように入力することで、それを証明できます。

 >>> 3*3 9 >>> 3**3 27

注:このチュートリアルに従うには、Python3をインストールする必要があります。 インストールされていない場合は、Pythonインストールガイドを確認してください。

ご覧のとおり、最初の数字の後と2番目の数字の前にアスタリスクを使用しています。 これを見ると、算術演算子を使用していることを意味します。

一方、解凍する反復可能オブジェクトの前にアスタリスク(*、**)を使用します(例:

 >>> *range(1, 6), (1, 2, 3, 4, 5) >>> {**{'vanilla':3, 'chocolate':2}, 'strawberry':2} {'vanilla': 3, 'chocolate': 2, 'strawberry': 2}

取得しなくても心配しないでください。これはPythonでアンパックするための前文にすぎません。 さあ、チュートリアル全体を読んでください!

開梱とは何ですか?

アンパッキングは、リスト、タプル、辞書などの反復可能なものを取り出すプロセスです。 箱を開けて、ケーブル、ヘッドホン、USBなどのさまざまなアイテムを取り出すことと考えてください。

開梱ボックスの画像
Pythonでのアンパックは、実際のボックスのアンパックに似ています。

理解を深めるために、この同じ例をコードに変換してみましょう。

 >>> mybox = ['cables', 'headphones', 'USB'] >>> item1, item2, item3 = mybox

ご覧のとおり、 myboxリスト内の3つのアイテムを3つの変数item1、item2、item2に割り当てています。 この種の変数代入は、Pythonでのアンパッキングの基本的な概念です。

各アイテムの値を取得しようとすると、 item1は「ケーブル」を指し、 item2は「ヘッドフォン」を指します。

 >>> item1 'cables' >>> item2 'headphones' >>> item3 'USB'

ここまでは、このコードですべてがうまくいくように見えますが、同じ量の割り当てられた変数を維持しながら、より多くの要素を含むリストを解凍したい場合はどうでしょうか。

 >>> newbox = ['cables', 'headphones', 'USB', 'mouse'] >>> item1, item2, item3 = newbox Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: too many values to unpack (expected 3)

おそらく、この種のエラーを予期していたのでしょう。 基本的に、4つのリストアイテムを3つの変数に割り当てていますが、Pythonはどのようにして適切な値を割り当てることができますか?

そうではありません。これは、「解凍するには値が多すぎます」というメッセージとともにValueErrorが発生するためです。 これは、左側に3つの変数を設定し、右側に4つの値(newboxリストに対応)を設定しているために発生しています。

同様のプロセスを実行しようとすると、アンパックする値よりも多くの変数を使用すると、わずかに異なるメッセージを除いて、別のValueErrorが発生します。

 >>> lastbox = ['cables', 'headphones'] >>> item1, item2, item3 = lastbox Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: not enough values to unpack (expected 3, got 2)

注:私たちはリストを扱ってきましたが、この形式のアンパックは任意の反復可能(リスト、セット、タプル、辞書)で使用できます

では、どうすればこの状況を克服できるでしょうか。 エラーが発生することなく、いくつかの変数に対して反復可能なすべてのアイテムを解凍する方法はありますか?

確かにあり、それはアンパッキング演算子またはアスタリスク演算子(*、**)と呼ばれます。 Pythonでの使用方法を見てみましょう。

*演算子を使用してリストを解凍する方法

アスタリスク演算子(*)は、まだ割り当てられていないiterableのすべての値を解凍するために使用されます。

インデックスを使用せずにリストの最初と最後の要素を取得したいとします。アスタリスク演算子を使用して取得できます。

 >>> first, *unused, last = [1, 2, 3, 5, 7] >>> first 1 >>> last 7 >>> unused [2, 3, 5]

ご存知のように、アスタリスク演算子を使用して未使用の値をすべて取得します。 値を破棄するための推奨される方法は、アンダースコア変数(_)を使用することです。これは、「ダミー変数」として使用されることもあります。

 >>> first, *_, last = [1, 2, 3, 5, 7] >>> _ [2, 3, 5]

リストに2つの要素しかない場合でも、このトリックを使用できます。

 >>> first, *_, last = [1, 2] >>> first 1 >>> last 2 >>> _ []

この場合、アンダースコア変数(ダミー変数)は空のリストを格納するため、それらの周りの他の2つの変数はリストの使用可能な値にアクセスできます。

一般的なトラブルシューティング

iterableのユニークな要素をアンパックできます。 たとえば、次のようなものを思い付くでしょう。

 >>> *string = 'PythonIsTheBest'

ただし、上記のコードはSyntaxErrorを返します

 >>> *string = 'PythonIsTheBest' File "<stdin>", line 1 SyntaxError: starred assignment target must be in a list or tuple

これは、PEP仕様によると次の理由によるものです。

単純な代入の左側にあるタプル(またはリスト)

iterableのすべての値を単一の変数にアンパックする場合は、タプルを設定する必要があるため、単純なコンマを追加するだけで十分です。

 >>> *string, = 'PythonIsTheBest' >>> string ['P', 'y', 't', 'h', 'o', 'n', 'I', 's', 'T', 'h', 'e', 'B', 'e', 's', 't']

別の例は、数列を返す範囲関数を使用することです。

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

リストとタプルをアスタリスクで解凍する方法がわかったので、次に辞書の解凍に取り掛かります。

**演算子を使用して辞書を解凍する方法

リストとタプルを解凍するために単一のアスタリスクが使用され、辞書を解凍するために二重のアスタリスク(**)が使用されます。

残念ながら、タプルとリストで行ってきたように、辞書を単一の変数に解凍することはできません。 これは、以下がエラーをスローすることを意味します。

 >>> **greetings, = {'hello': 'HELLO', 'bye':'BYE'} ... SyntaxError: invalid syntax

ただし、呼び出し可能オブジェクトやその他の辞書内で**演算子を使用することはできます。 たとえば、他の辞書から作成されたマージされた辞書を作成する場合は、次のコードを使用できます。

 >>> food = {'fish':3, 'meat':5, 'pasta':9} >>> colors = {'red': 'intensity', 'yellow':'happiness'} >>> merged_dict = {**food, **colors} >>> merged_dict {'fish': 3, 'meat': 5, 'pasta': 9, 'red': 'intensity', 'yellow': 'happiness'}

これは複合辞書を作成するための非常に短い方法ですが、これはPythonでアンパックする主なアプローチではありません。

呼び出し可能オブジェクトでアンパッキングを使用する方法を見てみましょう

関数のパッキング:argsとkwargs

クラスまたは関数に実装される前に、argsとkwargsを見たことがあるでしょう。 なぜそれらを呼び出し可能オブジェクトと一緒に使用する必要があるのか​​を見てみましょう。

*演算子(args)を使用したパッキング

2つの数値の積を計算する関数があるとします。

 >>> def product(n1, n2): ... return n1 * n2 ... >>> numbers = [12, 1] >>> product(*numbers) 12

ご覧のとおり、関数にリスト番号をアンパックしているので、実際には次のように実行しています。

 >>> product(12, 1) 12

ここまではすべて問題なく動作しますが、もっと長いリストを渡したい場合はどうでしょうか。 関数が管理できるよりも多くの引数を受け取っているため、間違いなくエラーが発生します。

 >>> numbers = [12, 1, 3, 4] >>> product(*numbers) ... TypeError: product() takes 2 positional arguments but 4 were given

リストを関数に直接パックするだけで、これらすべてを解決できます。これにより、関数内に反復可能オブジェクトが作成され、関数に任意の数の引数を渡すことができます。

 >>> def product(*args): ... result = 1 ... for i in args: ... result *= i ... return result ... >>> product(*numbers) 144

ここでは、 argsパラメーターを反復可能として扱い、その要素をウォークスルーして、すべての数値の積を返します。 結果の開始番号は1でなければならないことに注意してください。ゼロから開始すると、関数は常にゼロを返すためです。

注:argsは単なる慣例であり、他のパラメーター名を使用できます

組み込みのprint関数と同様に、リストを使用せずに任意の数値を関数に渡すこともできます。

 >>> product(5, 5, 5) 125 >>> print(5, 5, 5) 5 5 5

最後に、関数の引数のオブジェクト型を取得しましょう。

 >>> def test_type(*args): ... print(type(args)) ... print(args) ... >>> test_type(1, 2, 4, 'a string') <class 'tuple'> (1, 2, 4, 'a string')

上記のコードで述べたように、 argsのタイプは常にタプルであり、その内容は関数に渡されるすべての非キーワード引数になります。

**演算子を使用したパッキング(kwargs)

前に見たように、**演算子は辞書専用に使用されます。 これは、この演算子を使用して、キーと値のペアをパラメーターとして関数に渡すことができることを意味します。

位置引数「name」と未定義の量のキーワード付き引数を受け取る関数make_personを作成してみましょう。

 >>> def make_person(name, **kwargs): ... result = name + ': ' ... for key, value in kwargs.items(): ... result += f'{key} = {value}, ' ... return result ... >>> make_person('Melissa', id=12112, location='london', net_worth=12000) 'Melissa: id = 12112, location = london, net_worth = 12000, '

ご覧のとおり、 ** kwargsステートメントは、キーワード付きのすべての引数を辞書に変換します。辞書は、関数内で繰り返すことができます。

注:kwargsは単なる規則であり、このパラメーターに任意の名前を付けることができます

argsで行ったのと同じ方法で、 kwargsのタイプを確認できます。

 >>> def test_kwargs(**kwargs): ... print(type(kwargs)) ... print(kwargs) ... >>> test_kwargs(random=12, parameters=21) <class 'dict'> {'random': 12, 'parameters': 21}

kwargs内部変数は常に辞書になり、関数に渡されたキーと値のペアが格納されます。

最後に、同じ関数でargskwargsを利用しましょう。

 >>> def my_final_function(*args, **kwargs): ... print('Type args: ', type(args)) ... print('args: ', args) ... print('Type kwargs: ', type(kwargs)) ... print('kwargs: ', kwargs) ... >>> my_final_function('Python', 'The', 'Best', language='Python', users='A lot') Type args: <class 'tuple'> args: ('Python', 'The', 'Best') Type kwargs: <class 'dict'> kwargs: {'language': 'Python', 'users': 'A lot'}

結論

アンパック演算子は、日常のタスクで非常に役立ちます。これで、個々のステートメントと関数パラメーターの両方で演算子を使用する方法がわかりました。

このチュートリアルでは、次のことを学びました。

  • タプルとリストには*を使用し、辞書には**を使用します
  • 関数およびクラスコンストラクターでアンパッキング演算子を使用できます
  • argsは、キーワード以外のパラメーターを関数に渡すために使用されます
  • kwargsは、キーワード付きパラメーターを関数に渡すために使用されます。