Python *args と **kwargs の説明: 柔軟な関数と明確な例

  1. ホーム
  2. ブログ
  3. ソフトウェア開発

ソフトウェア開発

Python *args と **kwargs の説明: 明確な例による柔軟な関数

関数シグネチャにあるこれらの星は、見た目ほど神秘的ではありません。 *args と **kwargs が引数を収集する方法、同じスターが引数を解凍する方法、および悪用しにくい関数シグネチャを設計する方法を学びます。

読了目安8分
Python *args と **kwargs の説明: 柔軟な関数と明確な例

ポイント

  • *args は追加の位置引数を タプル に収集します。
  • **kwargs は追加のキーワード引数を dictionary に収集します。
  • * と ** が重要であるため、名前は単なる慣例です。
  • 同じスターは、関数を呼び出すときにリストまたは辞書を 解凍します。

遅かれ早かれ、def handler(*args, **kwargs) のような関数を読んで、それが何を受け入れるのか疑問に思うでしょう。 Python *args と **kwargs を使用すると、関数は柔軟な数の引数を受け取ることができます。これらは、デコレータ、ラッパー、および多くのライブラリ API を強化します。

このガイドでは、Python が関数の呼び出し方法を制御できる新しい方法とともに、それらが両方向でどのように機能するかについて説明します。各例を Python 3.13 で実行し、示されている出力はその実行からのものです。

*args の機能

星 1 つで記述されたパラメータは、追加の位置引数をタプルに収集します。

def total(*args):
    print(type(args).__name__, args)
    return sum(args)

print(total(1, 2, 3))
# tuple (1, 2, 3)
# 6

print(total())
# tuple ()
# 0

引数を指定しない場合は、単に空のタプルを取得するだけなので、関数は引き続き機能します。 args という名前は慣例です。 *numbers も同じように動作しますが、多くの場合、よりわかりやすい名前の方が適しています。

**kwargs が行うこと

2 つの星は、追加のキーワード引数を辞書に収集します。

def describe(**kwargs):
    print(type(kwargs).__name__, kwargs)

describe(name="Asha", city="Pune")
# dict {'name': 'Asha', 'city': 'Pune'}

これは通常の 辞書 であるため、ループして get() でキーを検索し、渡すことができます。キーは渡された順序を維持します。

パラメータの順序

さまざまな種類のパラメーターを混合する場合、Python では特定の順序が必要です。

  1. 通常の位置パラメータ
  2. *引数
  3. キーワードのみのパラメータ
  4. **クワーグス
def order(item, qty=1, *extras, priority=False, **notes):
    print(item, qty, extras, priority, notes)

order("pen")
# pen 1 () False {}

order("pen", 2, "gift wrap", "card", priority=True, note="leave at door")
# pen 2 ('gift wrap', 'card') True {'note': 'leave at door'}

priority に注目してください。 *extras の後にあるため、名前でのみ設定できるため、位置引数が間違って設定されてしまう可能性はありません。 **kwargs を前に置くことは許可されておらず、テストでは SyntaxError:「引数を var-keyword 引数の後に続けることはできません」が発生しました。

電話時の開梱

同じスターがコール サイトで逆に機能します。 1 つの星はリストまたはタプルを位置引数に展開し、2 つの星は辞書をキーワード引数に展開します。

nums = [4, 5, 6]
total(*nums)            # same as total(4, 5, 6) -> 15

settings = {"name": "Ben", "city": "Delhi"}
describe(**settings)    # same as describe(name="Ben", city="Delhi")

アンパックは、*args と **kwargs を使用する関数だけでなく、どの関数でも機能します。これは、設定が辞書として届くときに便利です。

def connect(host, port, timeout=5):
    return f"{host}:{port} (timeout {timeout})"

connect(**{"host": "db.local", "port": 5432})
# 'db.local:5432 (timeout 5)'

connect(**{"host": "db.local", "port": 5432, "ssl": True})
# TypeError: connect() got an unexpected keyword argument 'ssl'

そのエラーは役に立ちます。 **kwargs のない関数は不明なオプションを拒否するため、設定名のタイプミスは黙って無視されるのではなく、大声で失敗します。

キーワードのみの引数

署名内の名前のない裸の * は、「これ以降はすべてキーワードで渡す必要がある」ことを意味します。

def make_user(name, *, admin=False):
    return name, admin

make_user("Asha", admin=True)
# ('Asha', True)

make_user("Asha", True)
# TypeError: make_user() takes 1 positional argument but 2 were given

これはブール フラグを保護する優れた方法です。 make_user("Asha", True) のような呼び出しでは、読者は True が何を意味するかを推測する必要があります。 admin=True を要求すると、すべての呼び出しが一目瞭然になります。

位置のみの引数

Python 3.8 以降、/ は位置によってのみ渡すことができるパラメータをマークします。

def area(width, height, /):
    return width * height

area(3, 4)                 # 12
area(width=3, height=4)
# TypeError: area() got some positional-only arguments
#   passed as keyword arguments: 'width, height'

これにより、誰も名前に依存することができないため、ライブラリは誰のコードも壊すことなく、後でこれらのパラメータの名前を変更できます。いくつかの組み込みはこのように動作します。たとえば、len(obj=[1]) は機能しません。

ヒント: 完全なパターンは def f(pos_only, /, normal, *, kw_only) です。 3 つすべてが必要になることはほとんどありませんが、/ と * の意味を知っておくと、ドキュメントやライブラリの署名を読むのに役立ちます。

デコレータとラッパー

これが、両方を併用する現実的な主な理由です。ラッパーは多くの場合、元の関数が受け取る引数をすべて受け入れ、変更せずに渡す必要があります。

import functools, time

def timed(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        print(f"{func.__name__} took {time.perf_counter() - start:.4f}s")
        return result
    return wrapper

@timed
def slow_add(a, b, delay=0.01):
    time.sleep(delay)
    return a + b

print(slow_add(2, 3, delay=0.02))
# slow_add took 0.0205s
# 5

ラッパーは slow_add のパラメータをまったく知る必要がないため、どの関数でも機能します。タイミング図は若干異なります。 Python デコレータ に関するガイドでは、functools.wraps とその他の詳細について説明しています。

結合オプション

** は辞書リテラル内でも機能し、デフォルトに加えてオーバーライドを適用する巧妙な方法を提供します。

defaults = {"theme": "light", "lang": "en"}

def build(**overrides):
    return {**defaults, **overrides}

build(theme="dark")
# {'theme': 'dark', 'lang': 'en'}

後のキーが優先されるため、オーバーライドによってデフォルトが置き換えられます。 super().__init__(*args, **kwargs) のように、サブクラスもこのパターンを使用して、追加の引数を親クラスに渡します。 super() の詳細については、Python のクラスとオブジェクトを参照してください。

チートシート

構文 どこで 意味
def f(*args) 定義 追加の位置引数をタプルに収集する
デフ f(**クワーグス) 定義 追加のキーワード引数を辞書に収集する
def f(a, *, b) 定義 b はキーワードのみです
def f(a, /, b) 定義 a は位置のみです
f(*items) 電話をかける シーケンスを位置引数に展開する
f(**options) 電話をかける 辞書をキーワード引数に展開する
{**a、**b} リテラル 辞書を結合する
[*a、*b] リテラル イテラブルをリストに結合する

それらを避けるべき場合

柔軟性には代償が伴います。 def create(**kwargs) として定義された関数は、どのオプションが存在するかについて読者に何も伝えず、編集者はオプションをオートコンプリートできません。タイプミスは、何かが失敗する前に、いくつかのレイヤーを静かに通過する可能性があります。

したがって、関数に必要なものがわかっている場合は、明示的なパラメーターを使用することをおすすめします。ラッパー、デコレーター、別の関数への引数の転送、または total(*numbers) のような真の可変長入力など、本当に柔軟性が必要な場合は *args と **kwargs を使用してください。関数に多くの名前付きオプションが必要な場合は、dataclass を使用するとわかりやすい場合もあります。

よくある間違い

変更可能なデフォルトの使用

これは星の話ではありませんが、星の近くに現れることがよくあります。デフォルト値は関数の定義時に一度作成され、呼び出し間で共有されます。

def bad(items=[]):
    items.append(1)
    return items

print(bad(), bad())
# [1, 1] [1, 1]

どちらの呼び出しでもまったく同じリストが返され、その時点で 2 つの項目が保持されていました。 items=None を使用して、関数内に新しいリストを作成します。

その他のエラー

  • 解凍するのを忘れています。 total(nums) を呼び出すと、3 つの番号ではなく 1 つのリストが渡されます。 total(*nums)と書きます。
  • タイプミスを飲み込みます。 **kwargs は受け入れますが、不明なキーを無視する関数は、スペルミスのあるオプションを非表示にします。それらを検証するか、**kwargs を避けてください。
  • 同じ引数を 2 回渡す f(1, **{"a": 2}) (a が最初のパラメーター) と、「複数の値」エラーが発生します。
  • ラッパーで転送しません。 func(args, kwargs) を呼び出すと、タプルと辞書が渡されます。正しい形式はfunc(*args, **kwargs)です。

*args と **kwargs の型ヒント

*args と **kwargs のタイプ ヒントは、コレクション全体ではなく、個々の値を説明します。これは多くの人を驚かせます:

def total(*prices: float) -> float:
    return sum(prices)      # prices is tuple[float, ...]

def tag(**attrs: str) -> str:
    return " ".join(f'{k}="{v}"' for k, v in attrs.items())   # dict[str, str]

total(1.5, 2.5)                  # 4.0
tag(id="x", role="button")       # id="x" role="button"

ここで、prices は浮動小数点数のタプルとして扱われ、attrs は文字列の辞書として扱われます。キーワード引数に異なる型がある場合、Python 3.12 では TypedDict と Unpack を組み合わせた正確なオプションが追加されました。

from typing import TypedDict, Unpack

class Options(TypedDict, total=False):
    timeout: float
    retries: int

def fetch(url: str, **opts: Unpack[Options]) -> None: ...

mypy や Pyright などの型チェッカーは、fetch(url, retries="3") やスペルミスのあるオプション名を拒否できますが、プレーンな **kwargs では決して拒否できません。ただし、実行時の動作は変わりません。 fetch("u", retries="3") を Python 3.13 で実行したところ、Python 自体は型ヒントを強制しないため、問題なく実行されました。

**kwargs で受け入れられるキーの長いリストを文書化している場合は、それらを明示的なキーワードのみのパラメーターに置き換えることを検討してください。編集者はそれらをオートコンプリートできるため、間違いはすぐに発見されます。

まとめ

2 つの方向を覚えておいてください。定義では、星は集まります。通話中に、彼らは開梱します。呼び出しを明確にするためにキーワードのみの引数を追加し、実際に引数を転送するコード用に **kwargs を予約します。練習するには、すべての呼び出しの引数を記録する小さなデコレーターを作成し、それをいくつかの異なる関数で使用します。

よくある質問

*args と **kwargs の違いは何ですか?

*args は、追加の位置引数をタプルに収集します。 **kwargs は、追加のキーワード引数を辞書に収集します。関数では両方を一緒に使用できます。

それらを args と kwargs と呼ぶ必要がありますか?

いいえ。スターが機能するため、*値または**オプションは同様に有効です。 args と kwargs は単に広く認識されている規則です。

*args と **kwargs はどのような順序で指定する必要がありますか?

最初に通常のパラメータ、次に *args、次にキーワードのみのパラメータ、最後に **kwargs です。それ以外の順序では SyntaxError が発生します。

関数定義内の単一の * は何を意味しますか?

裸の * は、その後のすべてのパラメータをキーワードで渡す必要があることを意味します。これにより、特にブール フラグの呼び出しがより明確になります。

Python 関数の / は何を意味しますか?

/ より前のパラメータは位置のみであるため、呼び出し元はパラメータを名前で渡すことはできません。この機能は Python 3.8 で追加されました。

学び続ける

Stack Quoraと連携する

テスト、WordPress、または SEO についてサポートが必要ですか?

私たちは、チームがより優れたソフトウェアを出荷し、サイトを成長させるのを支援します。あなたが取り組んでいることを教えてください。明確な次のステップを提案します。

読み続けてください

詳しいガイドはこちら テスト、AI、最新の開発

この記事は気に入りましたか?トピックを選択して、最新のチュートリアルに直接アクセスしてください。

人工知能

AI 幻覚の説明: 言語モデルが物事をでっち上げる理由とそれを軽減する方法

AI 幻覚について説明しました。幻覚とは何か、言語モデルが幻覚を生成する理由、一般的な種類、グラウンディングとプロンプトで幻覚を軽減する方法、そして私たちが実行した簡単なチェックです。

記事を読む →
DevOps

環境変数の説明: .env ファイル、シークレット、および設定が正しく行われている

実行した Python の例を使用して環境変数について説明します。環境変数の読み取りと設定、すべての値が文字列である理由、.env ファイル、プロセスの継承、シークレット、検証について説明します。

記事を読む →
ソフトウェア開発

API レート制限の説明: アルゴリズム、ヘッダー、および 429 エラーの処理

API レート制限については、実行したシミュレーションで説明します。トークン バケット、固定ウィンドウ、スライディング ウィンドウ、429 応答、レート制限ヘッダー、バックオフによる再試行、およびテスト制限です。

記事を読む →
テスト自動化

Python でのモッキング:unittest.mock、patch、およびテストを嘘にする間違い

Python でのunittest.mockによるモック、テスト済み: モックとreturn_value、side_effect、名前が使用されている場所のパッチ、仕様と自動仕様、日付のモック、およびよくある間違い。

記事を読む →
ソフトウェアテスト

コードカバレッジの説明: 回線カバレッジとブランチカバレッジ、および 100% の実際の意味

実際のcoverage.pyの実行でコードカバレッジを説明: 行、分岐、条件のカバレッジ、レポートの読み取り、100%のカバレッジでもバグを見逃す可能性がある理由、および賢明なターゲット。

記事を読む →
データベースとSQL

SQL における DELETE、TRUNCATE、DROP: 違い、ロールバック、およびそれぞれをいつ使用するか

DELETE、TRUNCATE、DROP について、テスト済み SQL を使用して説明します。それぞれの削除内容、ロールバック、トリガー、ID リセット、速度、PostgreSQL、MySQL、SQL Server、SQLite の違いについて説明します。

記事を読む →
データベースとSQL

SQL ビューの説明: 作成、使用、落とし穴の回避、例を示します

SQL ビューについて、実行したクエリとともに説明しました。ビューの作成、ライブ結果、列の非表示、パフォーマンス、更新可能なビューと具体化されたビュー、壊れた依存関係、ベスト プラクティスについて説明します。

記事を読む →
ソフトウェア開発

TypeScript インターフェイスと型: 実際の違い、コンパイラでテスト

TypeScript インターフェイスと型、実際のコンパイラでチェック: 両方でできること、片方だけでできること、宣言のマージ、拡張と交差、および従うべき簡単なルール。

記事を読む →
ソフトウェア開発

Python *args と **kwargs の説明: 柔軟な関数と明確な例

Python の *args と **kwargs について、実行したコードで説明しました。パッキングとアンパッキング、パラメーターの順序、キーワードのみの引数と位置のみの引数、デコレーター、一般的なエラーです。

記事を読む →
ソフトウェア開発

Python Lambda 関数: 構文、実際の用途、および使用すべきでない場合

Python のラムダ関数について、実行したコードとともに説明します。構文、キーによるソート、マップとフィルター、クロージャー、遅延バインディング トラップ、通常の def がより適切な選択である場合。

記事を読む →
人工知能

AIエージェントとは何ですか?シンプルなエージェント ループを実行できる仕組み

AIエージェントとは何ですか?エージェント ループ、ツール、メモリ、ガードレール、実行した小さな Python ループ、実際のリスク、およびシンプルなチャットボットがより良い選択である場合についてのわかりやすいガイドです。

記事を読む →
DevOps

モニタリングと可観測性: ログ、メトリクス、トレースによる本当の違い

モニタリングと可観測性について説明: 既知および未知の障害、ログ、メトリクスとトレース、チェックアウト インシデントのウォークスルー、ゴールデン シグナル、OpenTelemetry、および便利なアラート。

記事を読む →
DevOps

Docker ボリュームとバインド マウント: コンテナ データを正しい方法で保持する

Docker ボリュームとバインド マウントについて説明します。コンテナ データが消える理由、名前付きボリューム、バインド マウント、tmpfs、Compose の例、バックアップ、アクセス許可、およびそれぞれをいつ使用するかについて説明します。

記事を読む →
テスト自動化

TDD と BDD: 主な違い、1 つの機能が両方の方法で構築され、それぞれをいつ使用するか

TDD と BDD は、両方の方法で構築された 1 つの割引機能で説明されています。実行したコードによるレッド-グリーン-リファクタリング、Given-When-Then シナリオ、ツール、トレードオフ、およびそれぞれをいつ使用するかです。

記事を読む →
ソフトウェア開発

Git mergeとrebaseの違い:例と使い分け

Git mergeとrebaseの違いを、実際のログ出力、比較表、rebaseの黄金律、安全なチームワークフローで解説します。

記事を読む →
ソフトウェア開発

Pythonのリスト内包表記:構文・例・使うべきでない場面

Pythonのリスト内包表記を解説:構文、フィルタ、if-else、ネストしたループ、実測した処理時間、そして通常のforループの方が良い場面。

記事を読む →
データベースとSQL

SQL JOINを例で解説(Inner・Left・Right・Full)

サンプルのテーブルとクエリでSQL JOINを順に解説。INNER、LEFT、RIGHT、FULL、CROSS、SELFに加え、よくある間違いとパフォーマンスのコツも紹介します。

記事を読む →