Skip to content

Python *args と **kwargs 完全ガイド:可変長引数をマスターしよう

Updated on

すべての Python 開発者は、いずれこの壁にぶつかります。ライブラリのソースコード、同僚のプルリクエスト、またはオープンソースプロジェクトを開くと、関数シグネチャ全体に *args**kwargs が散りばめられているのを目にします。アスタリスクは何を意味するのでしょうか?なぜ 1 つと 2 つがあるのでしょうか?どちらをいつ使えばいいのでしょうか?間違えると、「takes 2 positional arguments but 5 were given」や「got an unexpected keyword argument」といった混乱を招く TypeError メッセージが発生し、単純な関数呼び出しが突然デバッグセッションに変わってしまいます。

自分自身で柔軟な関数を書く必要があるとき、この問題はさらに悪化します。すべてのパラメータをハードコーディングすると API が硬直化します。多くの名前付きパラメータを受け入れると、関数シグネチャが読みにくくなります。明確さを犠牲にすることなく、可変個の引数を受け入れる関数を書く方法が必要です。

Python は 2 つの特別な構文機能でこれを解決します:可変長位置引数用の *args と、可変長キーワード引数用の **kwargs です。このガイドでは、両方を基礎から説明し、アンパック演算子をカバーし、実世界のパターンを歩きながら、最も一般的な間違いを避ける方法をお手伝いします。

📚

*args と **kwargs とは?

Python において、*args**kwargs は関数定義で可変個の引数を受け入れるための慣習です。

  • *args は追加の位置引数をタプルに収集します。
  • **kwargs は追加のキーワード引数を辞書に収集します。

argskwargs という名前は慣習であり、必須ではありません。魔法は *** プレフィックスから来ており、名前自体ではありません。*values**options と書いても、同じように動作します。

以下は最もシンプルな実演です:

def show_args(*args, **kwargs):
    print(f"args   = {args}")
    print(f"kwargs = {kwargs}")
 
show_args(1, 2, 3, name="Alice", age=30)
# args   = (1, 2, 3)
# kwargs = {'name': 'Alice', 'age': 30}

位置引数(名前なしで渡される値)は args にタプルとして入ります。キーワード引数(key=value 構文で渡される値)は kwargs に辞書として入ります。これが核心的な概念のすべてです。

*args の理解:可変長位置引数

パラメータ名の前の単一のアスタリスク * は、残りのすべての位置引数をタプルにパックするよう Python に指示します。これにより、関数は任意の数の位置値を受け入れることができます。

基本的な構文と使い方

def add_all(*args):
    """任意の数の値の合計を計算する"""
    total = 0
    for num in args:
        total += num
    return total
 
print(add_all(1, 2))            # 3
print(add_all(1, 2, 3, 4, 5))   # 15
print(add_all(10))               # 10
print(add_all())                 # 0

関数内部では、args は通常の Python タプルです。これを反復処理したり、インデックスでアクセスしたり、長さを確認したり、他の関数に渡したりできます。

def describe_args(*args):
    print(f"Type: {type(args)}")
    print(f"Length: {len(args)}")
    print(f"First element: {args[0] if args else 'N/A'}")
    print(f"Contents: {args}")
 
describe_args("hello", 42, True)
# Type: <class 'tuple'>
# Length: 3
# First element: hello
# Contents: ('hello', 42, True)

通常のパラメータと *args の組み合わせ

標準パラメータと *args を混在させることができます。すべての通常の位置パラメータが最初に埋められ、残りの位置引数は *args に入ります:

def log_message(level, *args):
    """重要度レベル付きでメッセージをログに記録する"""
    message = " ".join(str(a) for a in args)
    print(f"[{level.upper()}] {message}")
 
log_message("info", "Server started on port", 8080)
# [INFO] Server started on port 8080
 
log_message("error", "Connection failed:", "timeout after", 30, "seconds")
# [ERROR] Connection failed: timeout after 30 seconds

実例:柔軟な平均値関数

def average(*values):
    """任意の数の値の平均を計算する"""
    if not values:
        raise ValueError("average() requires at least one argument")
    return sum(values) / len(values)
 
print(average(85, 90, 78))         # 84.33333333333333
print(average(100))                 # 100.0
print(average(72, 88, 95, 67, 91)) # 82.6

実例:文字列フォーマットヘルパー

def build_path(*segments):
    """パスのセグメントをスラッシュで結合し、余分なものを削除する"""
    cleaned = [seg.strip("/") for seg in segments if seg]
    return "/" + "/".join(cleaned)
 
print(build_path("api", "v2", "users", "123"))
# /api/v2/users/123
 
print(build_path("/data/", "/reports/", "2026/", "sales.csv"))
# /data/reports/2026/sales.csv

**kwargs の理解:可変長キーワード引数

パラメータ名の前の二重アスタリスク ** は、残りのすべてのキーワード引数を辞書にパックするよう Python に指示します。これにより、関数は任意の数の名前付き値を受け入れることができます。

基本的な構文と使い方

def print_info(**kwargs):
    """キーと値のペアを整形して表示する"""
    for key, value in kwargs.items():
        print(f"  {key}: {value}")
 
print_info(name="Alice", age=30, city="Seattle")
#   name: Alice
#   age: 30
#   city: Seattle

関数内部では、kwargs は標準の Python 辞書です。.get().keys().values().items()、およびその他の辞書メソッドを使用できます。

def describe_kwargs(**kwargs):
    print(f"Type: {type(kwargs)}")
    print(f"Keys: {list(kwargs.keys())}")
    print(f"Values: {list(kwargs.values())}")
 
describe_kwargs(x=10, y=20, z=30)
# Type: <class 'dict'>
# Keys: ['x', 'y', 'z']
# Values: [10, 20, 30]

実例:設定ビルダー

def create_connection(host, port, **kwargs):
    """オプション設定付きでデータベース接続を作成する"""
    config = {
        "host": host,
        "port": port,
        "timeout": kwargs.get("timeout", 30),
        "retries": kwargs.get("retries", 3),
        "ssl": kwargs.get("ssl", True),
        "pool_size": kwargs.get("pool_size", 5),
    }
 
    # 呼び出し元が提供した余分な設定を追加
    for key, value in kwargs.items():
        if key not in config:
            config[key] = value
 
    return config
 
# 基本的な使い方
basic = create_connection("localhost", 5432)
print(basic)
# {'host': 'localhost', 'port': 5432, 'timeout': 30, 'retries': 3, 'ssl': True, 'pool_size': 5}
 
# カスタムオプション付き
custom = create_connection(
    "db.example.com", 5432,
    timeout=60,
    ssl=False,
    application_name="my_app"
)
print(custom)
# {'host': 'db.example.com', 'port': 5432, 'timeout': 60, 'retries': 3, 'ssl': False, 'pool_size': 5, 'application_name': 'my_app'}

実例:HTML タグビルダー

def html_tag(tag, content="", **attributes):
    """オプション属性付きの HTML タグを生成する"""
    attr_str = ""
    for key, value in attributes.items():
        # Python の命名規則を HTML に変換(class_ -> class)
        html_key = key.rstrip("_")
        attr_str += f' {html_key}="{value}"'
 
    if content:
        return f"<{tag}{attr_str}>{content}</{tag}>"
    return f"<{tag}{attr_str} />"
 
print(html_tag("a", "Click here", href="https://example.com", class_="btn"))
# <a href="https://example.com" class="btn">Click here</a>
 
print(html_tag("img", src="photo.jpg", alt="A photo", width="200"))
# <img src="photo.jpg" alt="A photo" width="200" />
 
print(html_tag("p", "Hello world", id="intro", style="color: blue"))
# <p id="intro" style="color: blue">Hello world</p>

*args と **kwargs の併用

同じ関数定義で両方を使用して、位置引数とキーワード引数の任意の組み合わせを受け入れることができます。パラメータの順序には厳格なルールがあります。

パラメータの順序ルール

Python は関数シグネチャで以下の厳密な順序を強制します:

  1. 通常の位置パラメータ
  2. *args(可変長位置)
  3. キーワード専用パラメータ(*args の後)
  4. **kwargs(可変長キーワード)
def example(a, b, *args, option=True, **kwargs):
    print(f"a = {a}")
    print(f"b = {b}")
    print(f"args = {args}")
    print(f"option = {option}")
    print(f"kwargs = {kwargs}")
 
example(1, 2, 3, 4, 5, option=False, color="red", size=10)
# a = 1
# b = 2
# args = (3, 4, 5)
# option = False
# kwargs = {'color': 'red', 'size': 10}

パラメータの種類とその順序の要約は以下の通りです:

位置種類構文説明
1番目位置引数parama, b必須、位置で指定
2番目デフォルト引数param=valuec=10オプション、位置または名前で指定可能
3番目可変長位置引数*args*args余分な位置引数を収集
4番目キーワード専用引数param (* の後)option=True名前で指定する必要がある
5番目可変長キーワード引数**kwargs**kwargs余分なキーワード引数を収集

よくあるパターン:パススルー関数

*args**kwargs を使った最も有用なパターンの 1 つは、すべての引数を別の関数にそのまま渡す関数を作成することです:

def timed_call(func, *args, **kwargs):
    """関数を呼び出し、実行時間を計測する"""
    import time
    start = time.perf_counter()
    result = func(*args, **kwargs)
    elapsed = time.perf_counter() - start
    print(f"{func.__name__} took {elapsed:.4f}s")
    return result
 
def expensive_sum(a, b, c):
    import time
    time.sleep(0.1)
    return a + b + c
 
result = timed_call(expensive_sum, 10, 20, c=30)
# expensive_sum took 0.1003s
print(result)
# 60

* と ** を使ったアンパック(展開)

*** 演算子は双方向に機能します:関数定義では引数をパックし、関数呼び出しやその他のコンテキストでは引数をアンパック(展開)します。

リストとタプルのアンパック

イテラブルを個別の位置引数に展開するために * を使用します:

def add(a, b, c):
    return a + b + c
 
numbers = [10, 20, 30]
 
# アンパックなし - これは TypeError を引き起こす
# add(numbers)  # TypeError: add() missing 2 required positional arguments
 
# アンパックあり - リストを別々の引数に展開
result = add(*numbers)
print(result)  # 60
 
# タプル、セット、および任意のイテラブルで動作
coords = (5, 10, 15)
print(add(*coords))  # 30

* は代入やリスト構築でも使用できます(Python 3.5+):

# 代入での拡張アンパック
first, *middle, last = [1, 2, 3, 4, 5]
print(first)   # 1
print(middle)  # [2, 3, 4]
print(last)    # 5
 
# リスト/タプル構築でのアンパック
list_a = [1, 2, 3]
list_b = [4, 5, 6]
combined = [*list_a, *list_b]
print(combined)  # [1, 2, 3, 4, 5, 6]
 
# 追加要素付きのアンパック
extended = [0, *list_a, 99, *list_b, 100]
print(extended)  # [0, 1, 2, 3, 99, 4, 5, 6, 100]

辞書のアンパック

辞書をキーワード引数に展開するために ** を使用します:

def create_user(name, email, role="viewer"):
    return {"name": name, "email": email, "role": role}
 
user_data = {"name": "Alice", "email": "alice@example.com", "role": "admin"}
 
# 辞書をキーワード引数に展開
user = create_user(**user_data)
print(user)
# {'name': 'Alice', 'email': 'alice@example.com', 'role': 'admin'}

** を使った辞書のマージ

** の最も実用的な用途の 1 つは辞書のマージです:

defaults = {"color": "blue", "size": 12, "font": "Arial"}
user_prefs = {"color": "red", "size": 16}
 
# マージ:user_prefs が defaults を上書き
merged = {**defaults, **user_prefs}
print(merged)
# {'color': 'red', 'size': 16, 'font': 'Arial'}
 
# Python 3.9+ では | 演算子もサポート
merged_new = defaults | user_prefs
print(merged_new)
# {'color': 'red', 'size': 16, 'font': 'Arial'}
 
# マージ時に追加のキーを追加
final = {**defaults, **user_prefs, "theme": "dark"}
print(final)
# {'color': 'red', 'size': 16, 'font': 'Arial', 'theme': 'dark'}

* と ** アンパックの組み合わせ

関数呼び出し時に両方の演算子を一緒に使用できます:

def report(title, *items, separator="---", **metadata):
    print(f"== {title} ==")
    for item in items:
        print(f"  - {item}")
    print(separator)
    for key, value in metadata.items():
        print(f"  {key}: {value}")
 
positional = ["Task A", "Task B", "Task C"]
options = {"author": "Alice", "date": "2026-02-14"}
 
report("Sprint Review", *positional, separator="===", **options)
# == Sprint Review ==
#   - Task A
#   - Task B
#   - Task C
# ===
#   author: Alice
#   date: 2026-02-14

実践的なパターン

パターン 1:デコレータ関数

本番の Python で *args**kwargs の最も一般的な用途は、デコレータを書くことです。デコレータは 1 つの関数を別の関数でラップします。ラップされる関数のシグネチャが事前にわからないため、すべての引数を転送するために *args**kwargs を使用する必要があります:

import functools
import time
 
def retry(max_attempts=3, delay=1.0):
    """失敗時に関数を最大 max_attempts 回リトライする"""
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            last_exception = None
            for attempt in range(1, max_attempts + 1):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    last_exception = e
                    print(f"Attempt {attempt}/{max_attempts} failed: {e}")
                    if attempt < max_attempts:
                        time.sleep(delay)
            raise last_exception
        return wrapper
    return decorator
 
@retry(max_attempts=3, delay=0.5)
def fetch_data(url, timeout=10):
    """失敗する可能性のあるデータ取得をシミュレート"""
    import random
    if random.random() < 0.6:
        raise ConnectionError(f"Failed to connect to {url}")
    return f"Data from {url}"
 
# デコレータは url と timeout を *args/**kwargs 経由で転送
result = fetch_data("https://api.example.com", timeout=5)
print(result)

パターン 2:サブクラスの init 転送

サブクラス化時に、コンストラクタ引数を親クラスに転送する必要があることがよくあります。*args**kwargs はこれをきれいにします:

class Animal:
    def __init__(self, name, species, sound="..."):
        self.name = name
        self.species = species
        self.sound = sound
 
    def speak(self):
        return f"{self.name} says {self.sound}"
 
class Dog(Animal):
    def __init__(self, *args, breed="Unknown", **kwargs):
        super().__init__(*args, **kwargs)
        self.breed = breed
 
    def info(self):
        return f"{self.name} ({self.breed}) - {self.species}"
 
# すべての Animal パラメータがシームレスに渡される
dog = Dog("Rex", "Canine", sound="Woof!", breed="German Shepherd")
print(dog.speak())  # Rex says Woof!
print(dog.info())   # Rex (German Shepherd) - Canine

このパターンは、Django、Flask、SQLAlchemy などのフレームワークで基底クラスを拡張する場合、複雑なクラス階層で作業するときに不可欠です。

パターン 3:ラッパーとプロキシ関数

元の関数を変更せずに関数呼び出しをインターセプトまたは変更する必要がある場合:

def log_call(func):
    """関数の呼び出しとその引数をログに記録する"""
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        args_repr = [repr(a) for a in args]
        kwargs_repr = [f"{k}={v!r}" for k, v in kwargs.items()]
        signature = ", ".join(args_repr + kwargs_repr)
        print(f"Calling {func.__name__}({signature})")
        result = func(*args, **kwargs)
        print(f"{func.__name__} returned {result!r}")
        return result
    return wrapper
 
@log_call
def calculate_discount(price, discount_pct, tax_rate=0.08):
    discounted = price * (1 - discount_pct / 100)
    return round(discounted * (1 + tax_rate), 2)
 
calculate_discount(100, 20, tax_rate=0.1)
# Calling calculate_discount(100, 20, tax_rate=0.1)
# calculate_discount returned 88.0

パターン 4:API クライアントビルダー

柔軟な API ラッパーを構築するのは古典的なユースケースです:

import json
 
class APIClient:
    def __init__(self, base_url, **default_headers):
        self.base_url = base_url.rstrip("/")
        self.default_headers = {
            "Content-Type": "application/json",
            "Accept": "application/json",
            **default_headers,
        }
 
    def request(self, method, endpoint, *args, **kwargs):
        """マージされたヘッダーとパラメータでリクエストを構築する"""
        url = f"{self.base_url}/{endpoint.lstrip('/')}"
        headers = {**self.default_headers, **kwargs.pop("headers", {})}
 
        request_info = {
            "method": method,
            "url": url,
            "headers": headers,
            **kwargs,
        }
        print(json.dumps(request_info, indent=2, default=str))
        return request_info
 
    def get(self, endpoint, **kwargs):
        return self.request("GET", endpoint, **kwargs)
 
    def post(self, endpoint, **kwargs):
        return self.request("POST", endpoint, **kwargs)
 
# 使用例
client = APIClient(
    "https://api.example.com",
    Authorization="Bearer token123"
)
 
client.get("/users", params={"page": 1, "limit": 50})
client.post("/users", data={"name": "Alice"}, headers={"X-Request-ID": "abc123"})

パターン 5:データサイエンス -- 動的なプロットパラメータ

データ分析関数を構築する際、**kwargs を使用すると、基盤となるライブラリに設定を渡すことができます:

import pandas as pd
 
def analyze_column(df, column, **plot_kwargs):
    """データフレームのカラムを分析し、要約統計を生成する"""
    stats = {
        "count": df[column].count(),
        "mean": df[column].mean(),
        "std": df[column].std(),
        "min": df[column].min(),
        "max": df[column].max(),
    }
 
    print(f"\nAnalysis of '{column}':")
    for stat, value in stats.items():
        print(f"  {stat}: {value:.2f}")
 
    # 余分な kwargs をプロット関数に転送
    plot_defaults = {"kind": "hist", "bins": 20, "title": f"Distribution of {column}"}
    plot_config = {**plot_defaults, **plot_kwargs}
 
    # df[column].plot(**plot_config)  # matplotlib がインストールされている場合はコメントを外す
    print(f"  Plot config: {plot_config}")
    return stats
 
# サンプルデータの作成
df = pd.DataFrame({
    "revenue": [100, 250, 180, 320, 275, 410, 195, 360],
    "quantity": [5, 12, 8, 15, 13, 20, 9, 17],
})
 
# デフォルトの分析
analyze_column(df, "revenue")
 
# **kwargs を通じて渡されるカスタムプロット設定
analyze_column(df, "revenue", kind="box", color="steelblue", figsize=(10, 6))

Jupyter ノートブックで作業し、対話的に関数シグネチャを試したい場合、RunCell (opens in a new tab) は AI 搭載のノートブック環境を提供し、*args**kwargs のパターンをテストしたり、パラメータ処理のリアルタイム提案を得たり、ワークフローを離れることなく引数渡しの問題をデバッグしたりできます。

よくある間違いとその修正方法

以下は、Python 開発者が *args**kwargs で遭遇する最も頻繁なエラーと、その解決策です:

間違いエラーメッセージ原因修正方法
パラメータの順序が間違いSyntaxError: invalid syntax**kwargs*args の前に置いた常に順序を守る:通常、 *args、キーワード専用、**kwargs
リストをアンパックせずに渡すTypeError: func() missing required arguments*[1,2,3] の代わりに [1,2,3] を渡したアンパックを使用:func(*my_list)
重複したキーワード引数TypeError: got multiple values for argument 'x'位置引数と **kwargs の両方に同じキーがある位置引数と辞書キーの間に重複がないことを確認
kwargs を直接変更予期しない副作用kwargs 辞書を変更しているkwargs.copy() または {**kwargs, ...} を使用
super().init で *args を忘れるTypeError: __init__() missing arguments親クラスに args を転送していないsuper().__init__(*args, **kwargs) を使用
kwargs で可変デフォルトを使用呼び出し間で状態が共有されるdef func(data={})None をデフォルトに使用:def func(data=None)

例:重複したキーワード引数

def greet(name, greeting="Hello"):
    return f"{greeting}, {name}!"
 
data = {"name": "Alice", "greeting": "Hi"}
 
# 間違い:name が位置引数として AND **data 内の両方で渡されている
# greet("Alice", **data)
# TypeError: greet() got multiple values for argument 'name'
 
# 正しい:アンパックのみを通じて渡す
print(greet(**data))
# Hi, Alice!
 
# または:重複キーを削除
print(greet("Alice", **{"greeting": "Hi"}))
# Hi, Alice!

例:kwargs を安全に変更する

def process(name, **kwargs):
    # 間違い:kwargs を直接変更すると呼び出し元の辞書に影響する
    # kwargs["processed"] = True
 
    # 正しい:新しい辞書を作成
    config = {**kwargs, "processed": True}
    return {"name": name, **config}
 
settings = {"timeout": 30, "retries": 3}
result = process("task1", **settings)
print(result)
# {'name': 'task1', 'timeout': 30, 'retries': 3, 'processed': True}
 
# 元の辞書は変更されていない
print(settings)
# {'timeout': 30, 'retries': 3}

*args/**kwargs と他のアプローチの比較

*args**kwargs を他の代替手段の代わりにいつ使うべきか?以下は比較です:

アプローチ構文最適な用途欠点
*argsdef f(*args)未知の数の同じ型の位置値引数ごとの型ヒントなし、名前付きアクセスなし
**kwargsdef f(**kwargs)柔軟なオプション、他の関数へのパススルーIDE での自動補完なし、静的型チェックなし
明示的なパラメータdef f(a, b, c)既知で固定された引数セット硬直性;パラメータを追加すると既存の呼び出しが破壊される
デフォルトパラメータdef f(a, b=10)適切なデフォルト値を持つオプションパラメータそれでも事前にすべてのオプションを知る必要がある
リスト/タプルパラメータdef f(items: list)順序付けられた値のコレクション呼び出し元が明示的にリストを構築する必要がある
辞書パラメータdef f(options: dict)構造化された設定呼び出し元が明示的に辞書を構築する必要がある
TypedDict/dataclassdef f(config: Config)構造化され型安全な設定ボイラープレートが増加し、クラス定義が必要

一般的なガイドライン:

  • 引数セットが既知で安定している場合は明示的なパラメータを使用する。
  • 関数が自然に可変個の同じ型の値に対して動作する場合(print()max()min() など)は *args を使用する。
  • 別の関数にオプションを転送したり、柔軟な設定を受け入れたり、拡張可能な API を構築する必要がある場合は **kwargs を使用する。
  • デコレータ、プロキシ関数、または完全な引数転送が必要なクラス階層を書く場合は両方を使用する。
  • IDE の自動補完や静的型チェックを持つ構造化された設定が必要な場合は、TypedDict または dataclass を使用する。

*args と **kwargs で型ヒントを追加する

Python 3.11+ では、UnpackTypedDict を使用して *args**kwargs に型ヒントを追加できます:

# 基本的な型ヒント(すべての args が同じ型)
def add_numbers(*args: float) -> float:
    return sum(args)
 
def set_options(**kwargs: str) -> dict[str, str]:
    return kwargs
 
# Python 3.11+:TypedDict を使用した正確な kwargs 型付け
from typing import TypedDict, Unpack
 
class ConnectionOptions(TypedDict, total=False):
    timeout: int
    retries: int
    ssl: bool
 
def connect(host: str, port: int, **kwargs: Unpack[ConnectionOptions]) -> dict:
    return {"host": host, "port": port, **kwargs}
 
# IDE は有効なキーワード引数を認識するようになった
result = connect("localhost", 5432, timeout=30, ssl=True)
print(result)
# {'host': 'localhost', 'port': 5432, 'timeout': 30, 'ssl': True}

よくある質問

*args と **kwargs は何の略ですか?

argskwargs という名前は、それぞれ「arguments(引数)」と「keyword arguments(キーワード引数)」の略です。これらは純粋に慣習的な名前です。実際の魔法は *** プレフィックス演算子から来ています。単一のアスタリスク * は Python に追加の位置引数をタプルにパックするよう指示し、二重アスタリスク ** は追加のキーワード引数を辞書にパックします。これらの名前は実質的にすべての Python コードベース、チュートリアル、およびライブラリで使用されているのを目にします。なぜなら、普遍的に認識されている慣習だからです。しかし、言語はこれらの特定の名前を要求していません。

args と kwargs 以外の名前を使用できますか?

はい。argskwargs という名前は慣習であり、構文要件ではありません。アンパック動作は完全に *** プレフィックスから来ています。*values*items*numbers**options**config**params、またはその他の有効な Python 識別子を書くことができます。ただし、すべての Python 開発者がすぐに認識するため、ほとんどの場合 *args**kwargs を使用することを強く推奨します。ファイル処理関数での *paths や HTTP クライアントでの **headers など、より説明的な名前が本当に可読性を向上させる場合にのみ、カスタム名を使用してください。

Python 関数のパラメータの正しい順序は何ですか?

Python は厳格な順序を強制します:通常の位置パラメータが最初に来て、次に *args、次にキーワード専用パラメータ(デフォルト値付き)、最後に **kwargs です。完全な順序は:def func(pos1, pos2, default1=val, *args, kw_only1, kw_only2=val, **kwargs) です。この順序に違反すると SyntaxError が発生します。覚えやすいフレーズは「位置、スター-args、キーワード専用、ダブルスター-kwargs」-- 星の数が左から右に増加していきます。

いつ *args をリストパラメータの代わりに使うべきですか?

各引数が独立した値であり、呼び出し元がコンテナを構築せずに自然に渡せるべき場合は *args を使用します:print("a", "b", "c")print(["a", "b", "c"]) より自然です。呼び出し元がすでに変数としてコレクションを持っている場合、またはコレクションと他のパラメータを区別する必要がある場合はリストパラメータを使用します。max()min()print() のような組み込み関数は、呼び出し規約が自然に感じられるため *args を使用し、sorted(iterable) のような関数は入力が本質的にシーケンスであるため単一のイテラブルを受け取ります。

*args と **kwargs は遅いですか?

*args**kwargs のオーバーヘッドは最小限です。Python は各呼び出しで *args のタプルと **kwargs の辞書を作成し、これには小さなメモリ割り当てが含まれます。ベンチマークでは、明示的なパラメータと比較した差は通常、呼び出しあたり数百ナノ秒です。これは実質的にすべての実世界のコードでは無視できます。これを回避する前に、数百万回の呼び出しをタイトなループで行う必要があります。*args/**kwargs を避けるよりも、アルゴリズムの効率性と I/O の最適化に集中してください。これらが提供する柔軟性とコードの保守性は、マイクロなパフォーマンスコストをはるかに上回ります。

まとめ

Python の *args**kwargs は、言語の中で最も実用的な機能の 2 つです。これらは基本的な問題を解決します:可読性を犠牲にすることなく、可変個の引数を受け入れるのに十分柔軟な関数を書く方法。

重要なポイント:

  • *args は余分な位置引数をタプルに収集します。関数が任意の数の値を受け入れるべき場合に使用します。
  • **kwargs は余分なキーワード引数を辞書に収集します。柔軟なオプション、設定のパススルー、および拡張可能な API に使用します。
  • パラメータの順序は常に:通常、*args、キーワード専用、**kwargs です。
  • アンパックは、*** を関数呼び出し、リスト構築、および辞書のマージで使用します。
  • デコレータは最も重要な実世界のユースケースです。これらは関数のシグネチャに関係なく任意の関数をラップするために *args**kwargs に依存しています。

既知で安定したシグネチャを持つ関数には明示的なパラメータから始めましょう。柔軟性が必要な場合、つまりデコレータ、サブクラスの転送、ラッパー関数、および API ビルダーでは *args**kwargs を活用しましょう。パッキングとアンパックの仕組みを身に付ければ、よりクリーンで再利用可能な Python コードを書くことができるようになります。

📚