Python | 関数:デフォルト引数

Python
スポンサーリンク

概要(デフォルト引数は「省略しても動くための既定値」)

デフォルト引数は、関数定義の引数に初期値を設定しておき、呼び出し時にその引数を省略できる仕組みです。使いどころは「ほとんどのケースで同じ値」「オプション的な設定」。柔軟にしつつ、呼び出しを短くできます。

def greet(name="ゲスト"):
    return f"こんにちは、{name}さん!"

print(greet())         # 省略 → "ゲスト"
print(greet("花子"))   # 上書き → "花子"
Python

基本構文と引数の並び(ここが重要)

必須→デフォルトの順で並べる

「必須引数」を前に、「デフォルト引数」を後ろに並べます。これは文法ルールで、守らないとエラーになります。

def format_price(amount, currency="JPY", with_tax=False):
    total = round(amount * (1.1 if with_tax else 1))
    return f"{currency} {total}"

print(format_price(350))                 # JPY 350
print(format_price(350, with_tax=True))  # JPY 385
Python

位置引数・キーワード引数との併用

デフォルト引数は「省略可能」ですが、必要ならキーワード指定で明示的に渡すと読みやすくなります。

def power(x, exp=2):
    return x ** exp

print(power(3))           # 9(省略)
print(power(x=3, exp=4))  # 81(明示)
Python

位置限定/キーワード限定との組み合わせ

インターフェースを堅牢にしたいとき、「/」の左は位置のみ、「*」の右はキーワードのみにできます。

def scale(x, /, factor=1.0, *, rounding=None):
    y = x * factor
    return round(y, rounding) if rounding is not None else y

print(scale(10, 0.3))                    # 3.0(xは位置のみ)
print(scale(10, 0.3, rounding=2))        # 3.00(roundingはキーワードのみ)
Python

評価タイミングとミュータブルの罠(最重要ポイントの深掘り)

デフォルト値は「定義時に一度だけ」評価される

デフォルト値は def が実行された瞬間に一度だけ作られ、以降の呼び出しで使い回されます。時刻やランダムなど「呼ぶたびに変わってほしい値」をデフォルトに直接書くと意図とズレます。

import datetime

def timestamp(ts=datetime.datetime.now()):  # NG 例
    return ts  # 定義時の時刻で固定される
Python

必要なら None を使って「呼び出し時に初期化」します。

def timestamp(ts=None):
    if ts is None:
        ts = datetime.datetime.now()
    return ts
Python

ミュータブル(list/dict)をデフォルトにしない

list や dict をデフォルトにすると、呼び出し間で「同じオブジェクト」が使い回され、意図せず蓄積されます。

def append_item(item, bag=[]):  # NG
    bag.append(item)
    return bag

print(append_item(1))  # [1]
print(append_item(2))  # [1, 2] ← 前回の中身が残る
Python

必ず None を使って「その場で新規生成」しましょう。

def append_item(item, bag=None):
    if bag is None:
        bag = []
    bag.append(item)
    return bag
Python

設計の勘所(柔軟性・安全性・可読性)

オプションは「ブール・列挙・値」できれいに表現

設定フラグは with_tax=True のようにブールで、選択肢は文字列や Enum で。曖昧さを減らします。

def format_text(text, *, case="lower"):
    s = text.strip()
    if case == "lower":  return s.lower()
    if case == "upper":  return s.upper()
    return s
Python

デフォルトは「現実の既定値」を選ぶ

一番よく使う値をデフォルトにするほど、呼び出しが短く自然になります。逆に議論の余地がある“危険な既定値”は避ける(例:安全機能を既定でオフにしない)。

None を「未指定」の合図にする

値が意味を持つケースでは、None を「未指定」にし、渡されたときだけ上書きするパターンが強いです。

def connect(host="localhost", port=None):
    port = 443 if port is None else port
    return (host, port)
Python

実用テクニック(ログ・時間・関数の前設定)

ログ関数:レベルや時刻の既定値

import datetime

def log(msg, *, level="INFO", now=None):
    ts = now or datetime.datetime.now()
    print(f"{ts:%Y-%m-%d %H:%M:%S} [{level}] {msg}")

log("起動")                 # 既定レベル・現在時刻
log("失敗", level="ERROR")  # レベル上書き
Python

重みや税率など「業務既定値」を持つ

def revenue(orders, *, tax_rate=0.1):
    total = sum(o["qty"] * o["price"] for o in orders)
    return round(total * (1 + tax_rate))

print(revenue([{"qty":2,"price":350}]))          # tax 10% 既定
print(revenue([{"qty":2,"price":350}], tax_rate=0.08))  # 8% に変更
Python

関数の「事前設定」用ラッパ(部分適用もどき)

def render_button(label, *, cls="primary", disabled=False):
    return f'<button class="{cls}" disabled="{disabled}">{label}</button>'

# 二次関数化せずとも、既定値で“事前設定”風に使える
print(render_button("OK"))                    # class=primary
print(render_button("Cancel", cls="secondary", disabled=True))
Python

つまずきやすいポイントと対策(順序・型・テスト)

引数の並び順を守る(必須→デフォルト→キーワード限定)

間違えると SyntaxError/TypeError。API を長期運用するなら、/ と * で「位置限定・キーワード限定」を活用して破壊的変更を防ぎます。

def api(user_id, /, *, page=1, limit=20):
    return (user_id, page, limit)
Python

型ヒントで「未指定」を表現

Optional(Union with None)で意図を明確にします。

from typing import Optional

def search(query: str, *, limit: Optional[int] = None) -> list[str]:
    # limit が None なら全件の上限を使う、など
    ...
Python

テストでは「既定」と「上書き」をそれぞれ検証

  • 既定値が期待通りに働くか
  • 上書き指定が優先されるか
  • ミュータブルの罠に陥っていないか(連続呼び出し)

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

例題1:デフォルトとキーワードの併用

def power(x, exp=2, *, mod=None):
    y = x ** exp
    return y % mod if mod else y

print(power(3))              # 9
print(power(3, 3))           # 27
print(power(3, mod=5))       # 4(9 % 5)
Python

例題2:None 初期化で時刻とリストを安全に

import datetime

def add_event(title, when=None, tags=None):
    when = when or datetime.datetime.now()
    tags = tags or []
    return {"title": title, "when": when, "tags": tags}

e1 = add_event("meet")
e2 = add_event("meet", tags=["work"])
print(e1["tags"], e2["tags"])  # [], ['work'](共有されない)
Python

例題3:位置限定+キーワード限定で安全な API

def resize(width, height, /, *, keep_ratio=True, background="black"):
    return {"w": width, "h": height, "keep": keep_ratio, "bg": background}

print(resize(640, 480))                         # 既定適用
print(resize(640, 480, background="white"))     # キーワードで上書き
Python

例題4:業務既定値の集中管理

DEFAULTS = {"currency": "JPY", "tax_rate": 0.1}

def invoice(amount, *, currency=DEFAULTS["currency"], tax_rate=DEFAULTS["tax_rate"]):
    total = round(amount * (1 + tax_rate))
    return f"{currency} {total}"

print(invoice(1000))                    # JPY 1100
print(invoice(1000, tax_rate=0.08))     # JPY 1080
Python

まとめ

  • デフォルト引数は「省略できる入口」。必須→デフォルトの順で並べ、キーワード指定で読みやすく。
  • デフォルト値は定義時に一度だけ評価。時刻・乱数・ミュータブルは None で受けて「呼び出し時に初期化」する。
  • / と * で位置限定・キーワード限定を使い、API を堅牢に保つ。
  • オプションはわかりやすい既定値を選び、None を未指定の合図に。型ヒントで意図を明確化。
  • 既定・上書き・連続呼び出しのテストで、ミュータブルの罠と評価タイミングの問題を早期に潰す。

実務で効くのは「安全な既定」「明示的な上書き」「未指定=None」の三本柱。これを癖にすると、あなたの関数は使いやすく、事故らない。

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