Programmingフルスタック Python 開発者

Pythonにおけるシーケンスと辞書のアンパックに関する演算子 * と ** の動作メカニズムを説明してください。微妙なポイントは何で、どのように役立つ場合がありますか?

Hintsage AIアシスタントで面接を突破

回答。

質問の背景:

* および ** 演算子によるアンパックは Python に昔から存在していますが、バージョンごとに適用範囲が拡張されています(例えば、Python 3.5からは、*** を用いた複数コレクションの結合がサポートされました)。これらの演算子は、コレクションの操作を柔軟にするため、引数の渡し方や関数内での収集に役立ちます。

問題:

動的なシーケンスを操作し、不定の数の引数を渡すときには、手動でループを記述しコレクションのサイズを確認する必要が生じます。*** の使い方を誤ったり、その役割を区別しないと、簡単にエラーが発生します。

解決策:

演算子 * はシーケンス(リスト、タプル、セット)のアンパック用であり、演算子 ** は関数呼び出し時に辞書のアンパック用、または複数の辞書を結合する際に使用されます。これにより、引数を優雅に渡したり、コレクションをマージしたり、任意の構造を関数の引数に簡単に変換できます。

コードサンプル:

def foo(a, b, c): print(a, b, c) args = (1, 2, 3) foo(*args) # 1 2 3 params = {'a': 10, 'b': 20, 'c': 30} foo(**params) # 10 20 30 list1 = [1, 2] list2 = [3, 4] combined = [*list1, *list2] print(combined) # [1, 2, 3, 4]

主な特徴:

  • *sequence は要素を個別の位置引数として渡し、**dict は名前付き引数として渡します。
  • コレクションを優雅に結合およびコピーできます:[*a, *b]{**d1, **d2} のように。
  • アンパックは関数の定義/宣言時と呼び出し時の両方で使用できます。

騙しの質問。

混合コレクションに * と ** を使用できますか?

関数を呼び出す際、* は位置引数のみ、** は名前付き引数のみに機能します。アンパックされていない辞書を * として渡すか、シーケンスを ** として渡すと、エラーが発生します。

def foo(a, b): print(a, b) foo(*{'a': 1, 'b': 2}) # 出力: a b (辞書のキー、値ではない!)

**{**d1, d2} のように辞書を結合する際、キーが重複するとどうなりますか?

結果として、最後の辞書のそのキーの値が得られます。

d1 = {'x': 1, 'y': 2} d2 = {'y': 33, 'z': 44} merged = {**d1, **d2} print(merged) # {'x': 1, 'y': 33, 'z': 44}

リストまたは辞書内包表記の中で * と ** を使用できますか?

はい、Python 3.5 以降、これは合法です。例えば:

lst = [1, 2, *range(3, 6)] # [1, 2, 3, 4, 5] dct = {**{'a': 1}, 'b': 2, **{'c': 3}}

一般的なエラーとアンチパターン

  • * を通じて辞書を渡す(キーのみがアンパックされます)。
  • 名前付き引数の重複により TypeError が発生することがある。
  • mapping でないオブジェクトに対して ** を使用しようとする。

実際の例

ネガティブケース

デベロッパーが入力関数に辞書を渡し、* を使って展開しますが、** を使用していません。予期しない動作が発生し、関数には値ではなくキーが渡されます。

メリット:

コードがすぐにクラッシュしないため、「動いているように見える」

デメリット:

隠れたバグと期待されるロジックとの不一致。

ポジティブケース

**kwargs を通じて引数を正しく渡し、辞書の巧妙なマージ、およびシーケンスの動的な統合に * を使用します。

メリット:

最大の柔軟性、コードの簡潔さ、リファクタリングの簡単さ。

デメリット:

多数の引数とコレクションがある場合、名前と順序に注意を払わないとエラーが発生する可能性があります。