Python
 Computer >> コンピューター >  >> プログラミング >> Python

Pythonの引数パッキングとアンパッキング入門:*argsと**kwargsの使い方を徹底解説

はじめに

Pythonで少しでもプログラミングをしたことがある方なら、関数定義で「*args」や「**kwargs」という記述を目にしたことがあるでしょう。しかし、これらが正確には何を意味するのか、きちんと理解していない方も多いのではないでしょうか。

*演算子と**演算子は、使用する場所によって互いに補完的な異なる動作を行います。メソッド定義の中で使う場合と、メソッド呼び出しの中で使う場合で、役割が変わるのがポイントです。

パッキング(Packing)とは

メソッド定義で*演算子を使うと、次のようになります。

def __init__(self, *args, **kwargs):
    pass

この操作は「パッキング(packing)」と呼ばれます。メソッド呼び出し時に渡されたすべての引数を、argsという1つのタプルにまとめる(パックする)からです。変数名はargs以外でも構いませんが、argsが最も一般的でPythonらしい(Pythonic)書き方とされています。

なぜすべての引数を1つの変数にまとめる必要があるのか、具体例で見てみましょう。3つの引数を受け取る関数があり、その引数すべてを含むサイズ3のリストがあるとします。このリストをそのまま関数に渡すと、呼び出しが失敗しエラーが発生します。

例1:リストをそのまま渡した場合

# 3つの引数を受け取る関数
def func(a, b, c):
    print("{} {} {}".format(a, b, c))

# 引数のリスト
lst = ['python', 'java', 'csharp']

# リストをそのまま渡す
func(lst)

実行結果

TypeError: func() missing 2 required positional arguments: 'b' and 'c'

リスト全体が第1引数aに渡されてしまい、bとcが不足しているためTypeErrorが発生しました。

いったん引数をパックすると、通常のタプルではできない操作が可能になります。args[0]args[1]args[2]のようにインデックスでアクセスすれば、それぞれ1番目、2番目、3番目の引数を取得できます。さらに、argsタプルをリストに変換すれば、要素の変更・削除・追加といった操作も自由に行えます。

アンパッキング(Unpacking)とは

パックされた引数を別のメソッドに渡すには、「アンパッキング(unpacking)」が必要です。

def __init__(self, *args, **kwargs):
    # 何らかの処理
    super(VehicleClass, self).__init__(self, *args, **kwargs)
    # 後続の処理

ここでも*演算子が登場しますが、今回はメソッド呼び出しのコンテキストで使われています。この場合、argsを展開し、あたかも各変数が独立しているかのようにメソッドを呼び出します。以下の例で詳しく確認してみましょう。

例2:アンパッキングの基本

def func1(x, y, z):
    print(x)
    print(y)
    print(z)

def func2(*args):
    # 変更できるようにargsタプルをリストに変換
    args = list(args)
    args[0] = 'HTML'
    args[1] = 'CSS'
    args[2] = 'JavaScript'
    func1(*args)

func2('Python', 'Java', 'CSharp')

実行結果

HTML
CSS
JavaScript

この出力からわかるように、func1に渡す前に3つの引数すべてを変更できています。

同様の方法で、例1で発生したTypeErrorも解決できます。関数呼び出し時に*を付けてリストを展開すればよいのです。

例1の解決版

# 3つの引数を受け取る関数
def func(a, b, c):
    print("{} {} {}".format(a, b, c))

# 引数のリスト
lst = ['python', 'java', 'csharp']

# *を付けてリストを展開して渡す
func(*lst)

実行結果

python java csharp

可変長引数の合計を求める例

Python関数にいくつの引数を渡す必要があるかわからない場合でも、パッキングを使えばすべての引数を1つのタプルにまとめられます。

# パッキングを使って個数不明の引数を合計する関数
def Sum(*args):
    total = 0
    for i in range(0, len(args)):
        total = total + args[i]
    return total

# 動作確認
print("2つの引数の場合の合計:\n", Sum(9, 12))
print("5つの引数の場合の合計:\n", Sum(2, 3, 4, 5, 6))
print("6つの引数の場合の合計:\n", Sum(20, 30, 40, 12, 40, 54))

実行結果

2つの引数の場合の合計:
21
5つの引数の場合の合計:
20
6つの引数の場合の合計:
196

なお、組み込み関数のsum()と名前が衝突しないよう、実際のコードでは変数名に注意しましょう。

パッキングとアンパッキングの併用例

次のプログラムは、パッキングとアンパッキングを両方使った例です。

# 3つの引数を受け取る関数
def func1(x, y, z):
    print("第1引数:", x)
    print("\n第2引数:", y)
    print("\n第3引数:", z)

# パッキング:func2に渡されたすべての引数がタプルargsにまとめられる
def func2(*args):
    # 変更するためにargsタプルをリストに変換
    args = list(args)

    # args[0]とargs[1]を変更
    args[0] = 'Hello'
    args[1] = 'TutorialsPoint'

    # argsを展開(アンパック)してfunc1()を呼び出す
    func1(*args)

# 動作確認
func2("I", "Love", "Coding")

実行結果

第1引数: Hello

第2引数: TutorialsPoint

第3引数: Coding

辞書に対する**の使い方

*がリストやタプルに対して機能するのに対し、**は辞書(dict)に対して機能します。辞書を**で展開すると、キーと値のペアがキーワード引数として関数に渡されます。

# **を使った辞書アイテムのアンパッキングのデモ
def func(x, y, z):
    print("辞書の1番目の値:", x)
    print("\n辞書の2番目の値:", y)
    print("\n辞書の3番目の値:", z)

d = {'x': 27, 'y': 54, 'z': 81}
func(**d)

実行結果

辞書の1番目の値: 27

辞書の2番目の値: 54

辞書の3番目の値: 81

**dにより、辞書dの内容がx=27y=54z=81というキーワード引数として関数に渡されます。なお、辞書のキー名は関数の仮引数名と一致している必要がある点に注意してください。

主な活用シーン

  • ソケットプログラミング:サーバーに不特定多数(無限)のリクエストを送信する必要がある場面で活用されます。
  • DjangoなどのWebフレームワーク:ビュー関数に可変個の引数を渡すために使われます。
  • ラッパー関数:可変引数を受け渡す必要があるラッパー関数(デコレーターなど)で広く利用されています。

まとめ

メソッド定義での*と**は「パッキング」(複数の引数をタプルや辞書にまとめる)、メソッド呼び出しでの*と**は「アンパッキング」(タプルや辞書を個別の引数へ展開する)という、互いに補完的な役割を果たします。この仕組みを理解しておくと、柔軟な関数設計はもちろん、Djangoなど既存ライブラリのコード読解にも大きく役立ちます。ぜひ自分のコードでも活用してみてください。

  1. Pythonで学ぶ相関と回帰分析の基礎|Seabornを使ったデータ分析入門

    相関(Correlation)とは、2つのデータセット間に存在する依存関係を含む統計的な関係性を指します。一方、線形回帰(Linear Regression)は、従属変数と1つ以上の独立変数の間の関係を直線的なアプローチで捉える手法です。独立変数が1つの場合は「単回帰」、複数ある場合は「重回帰」と呼ばれます。相関とは何か依存現象の身近な例としては、親と子の身体的特徴(外見)の類似性や、商品の価格と供給量の関係などが挙げられます。価格が上がれば供給量が増える、といった具合に、変数同士が互いに影響し合う様子を統計的に表したものが相関です。本記事では、Pythonの可視化ライブラリ「Seaborn」

  2. Pythonにおける「+=」と「=+」の違いとは?演算子の動作をわかりやすく解説

    Pythonには見た目がよく似ているのに、まったく異なる動作をする演算子が存在します。その代表例が「+=」と「=+」です。この2つは一見同じように見えますが、実際には意味も役割も大きく違います。本記事では、それぞれの動作の仕組みと具体的な使い方を詳しく解説します。 「+=」は複合代入演算子(in-place演算) +=は、Pythonでは複合代入演算子と呼ばれるもので、内部的には object.__iadd__() メソッドへのシンタックスシュガー(糖衣構文)です。 公式ドキュメントには次のように記載されています。 これらのメソッドは、拡張された算術代入(+=、-=、*=、@=、/=、//=