Python | 関数:**kwargs

Python
スポンサーリンク

概要(**kwargs は「名前付きの任意個の引数」を辞書で受け取る仕組み)

**kwargs は、関数に渡された「キーワード引数(名前=値)」を、任意の個数まとめて受け取るための記法です。関数内では普通の辞書(dict)として扱えます。固定のオプションだけでなく、将来増えるかもしれない設定を柔軟に受けるときに役立ちます。

def show_info(**kwargs):
    print(kwargs, type(kwargs))

show_info(name="Hanako", age=20)  # {'name': 'Hanako', 'age': 20} <class 'dict'>
Python

基本構文と並び順(ここが重要)

固定引数・args・キーワード固定・*kwargs の順序

関数定義では、必ず「位置引数 → *args → キーワード引数 → **kwargs」の順に並べます。順序が崩れるとエラーになります。

def render(label, *classes, id=None, **attrs):
    cls = " ".join(classes)
    extra = " ".join(f'{k}="{v}"' for k, v in attrs.items())
    return f'<button id="{id}" class="{cls}" {extra}>{label}</button>'

print(render("OK", "primary", "rounded", id="ok", disabled=True))
Python

受け取りは辞書、その場で展開もできる

kwargs で受けた辞書はそのまま別関数へ「」で展開できます。ラッパ関数やフォワーディングに向いています。

def base_connect(host, port, secure=False, timeout=3):
    return (host, port, secure, timeout)

def connect_with_defaults(**kwargs):
    kwargs.setdefault("secure", True)   # 欠けていれば補う
    kwargs.setdefault("timeout", 5)
    return base_connect(**kwargs)

print(connect_with_defaults(host="example.com", port=443))
Python

具体的な使いどころ(拡張性・前処理・検証)

将来のオプション追加に強い API を作る

公開関数で受け取れるオプションが増えても、**kwargs なら既存の呼び出しは壊れません。必要なキーだけ取り出し、残りは無視する設計にできます。

def format_price(amount, **options):
    currency = options.get("currency", "JPY")
    tax_rate = options.get("tax_rate", 0.1)
    total = round(amount * (1 + tax_rate))
    return f"{currency} {total}"

print(format_price(350))
print(format_price(350, tax_rate=0.08))
Python

バリデーション(許可キー・型チェック)

受け取りが自由な分、早めのチェックで利用者の間違いを検出します。わかりやすいエラーメッセージが命です。

def save_user(**kwargs):
    allowed = {"name", "email", "age"}
    unknown = set(kwargs) - allowed
    if unknown:
        raise ValueError(f"未許可のキー: {', '.join(sorted(unknown))}")
    if "email" in kwargs and "@" not in kwargs["email"]:
        raise ValueError("email の形式が不正です")
    # ここで保存処理…
    return kwargs
Python

前処理+委譲(変換してから渡す)

到着したキーワードを正規化して、別関数へ委譲。責務分離で読みやすく、テストしやすくなります。

def normalize_opts(**kwargs):
    if "case" in kwargs:
        kwargs["case"] = kwargs["case"].lower()
    return kwargs

def process(text, **kwargs):
    opts = normalize_opts(**kwargs)
    if opts.get("case") == "upper":
        text = text.upper()
    elif opts.get("case") == "lower":
        text = text.lower()
    return text

print(process("Coffee", case="UPPER"))
Python

設計の勘所(使うべき場面と避ける場面を深掘り)

**kwargs を使うべき場面

  • オプションが多い、または増える見込みがある
  • 呼び出し側から柔軟な設定を受けたい(ログ、レンダリング、フォーマッタなど)
  • ライブラリのラッパで「未使用のオプションも受けてそのまま渡す」設計

**kwargs を避ける場面

  • 必須の引数や意味が確定した少数のオプションは、名前付き引数として明示した方が読みやすい
  • 何でも受けすぎると「何が使えるのか」が不透明になる。docstring と検証で輪郭をはっきりさせる

位置限定/キーワード限定との併用

API を壊さないための作法として、「/ の左は位置のみ」「* の右はキーワードのみ」を使うと、意図しない呼び出しを防げます。

def api(user_id, /, *, page=1, limit=20, **extra):
    # user_id は位置のみ、page/limit はキーワードのみ、extra は将来用
    return {"user_id": user_id, "page": page, "limit": limit, **extra}

実践例(現場ですぐ使えるパターン)

HTML 属性の合成(未指定はスキップ)

def tag(name, **attrs):
    parts = [f'{k}="{v}"' for k, v in attrs.items() if v is not None]
    return f"<{name} {' '.join(parts)}></{name}>"

print(tag("div", id="main", data_role="dialog"))
Python

ログ関数(レベル・タグ・メタを柔軟に)

def log(*parts, **meta):
    level = meta.pop("level", "INFO")
    msg = " ".join(map(str, parts))
    extras = " ".join(f'{k}={v}' for k, v in meta.items())
    print(f"[{level}] {msg} {extras}".strip())

log("start", level="INFO", job="batch", try=1)
Python

検索 API のラッパ(未知オプションを透過)

def base_search(query, **options):
    # 実際の検索機構へ options を渡す
    return {"q": query, "options": options}

def search(query, **kwargs):
    # 既定値を補ってから委譲
    kwargs.setdefault("limit", 20)
    kwargs.setdefault("case_insensitive", True)
    return base_search(query, **kwargs)

print(search("coffee", limit=5))
Python

つまずきやすいポイントと対策

キーワード指定でしか渡せない

**kwargs は「名前=値」で渡す前提。位置引数は *args で受けるか、固定引数にする。混同すると TypeError の原因になります。

受けたまま丸投げは危険

その関数が知らない・対応していないオプションまで丸投げすると、下流で落ちます。許可リスト・置換・削除で整えてから渡すのが安全です。

型ヒントと docstring で意図を明確化

「受ける可能性のあるキー」と「既定値・型」をドキュメントに書く。主要キーは引数として明示し、残りを **kwargs にするのがバランス良いです。

def resize(width: int, height: int, *, keep_ratio: bool = True, **opts) -> dict:
    """画像をリサイズする。主要オプション:
    keep_ratio: アスペクト比維持(既定 True)
    その他のオプションは **opts で受ける
    """
    bg = opts.get("background", "black")
    return {"w": width, "h": height, "keep": keep_ratio, "bg": bg}
Python

例題で身につける(定番から一歩先まで)

例題1:必須+拡張オプション

def export(path, **opts):
    fmt = opts.get("format", "csv")
    compress = opts.get("compress", False)
    return {"path": path, "format": fmt, "compress": compress}

print(export("out/data"))
print(export("out/data", format="json", compress=True))
Python

例題2:許可キーのバリデーション

def schedule(task, **opts):
    allowed = {"at", "retry", "timeout"}
    bad = set(opts) - allowed
    if bad:
        raise ValueError(f"未知のオプション: {', '.join(bad)}")
    return {"task": task, **opts}

print(schedule("backup", at="02:00", retry=3))
Python

例題3:フォーマッタ(欠損補完+処理切り替え)

def format_text(text, **opts):
    opts.setdefault("case", "lower")
    s = text.strip()
    if opts["case"] == "upper":
        s = s.upper()
    elif opts["case"] == "lower":
        s = s.lower()
    width = opts.get("width")
    return s[:width] if width else s

print(format_text("  Coffee  ", width=6, case="upper"))  # 'COFFEE'
Python

例題4:プロキシでそのまま透過(必要な置換のみ)

def fetch(url, **kwargs):
    kwargs.setdefault("timeout", 5)
    if "headers" in kwargs and "User-Agent" not in kwargs["headers"]:
        kwargs["headers"]["User-Agent"] = "MyClient/1.0"
    return {"url": url, "opts": kwargs}

print(fetch("https://example.com", headers={"Accept": "application/json"}))
Python

まとめ

kwargs は「名前付き引数を辞書で任意個受ける」ための強力な仕組みです。固定・args・キーワード固定・*kwargs の順序を守り、主要オプションは引数として明示、拡張可能な部分は **kwargs に逃がす。受けたら早めにバリデーション・既定値補完・正規化を行い、必要に応じて委譲で再利用する。ドキュメントと型ヒントで「何が受け取れるのか」を伝えれば、初心者でも安全で拡張性の高い関数インターフェースを設計できます。

タイトルとURLをコピーしました