Python講座 / 位置引数・キーワード引数・既定値を使う

関数の引数は位置または名前で渡せ、既定値を持つ入力は省略できます。呼び出すたびに新しい状態が必要な場合は、リストなどの可変オブジェクトを既定値へ直接置かないようにします。

引数の意味を呼び出し側で見えるようにする

位置引数は順番で、キーワード引数は仮引数名で対応付けます。同じ仮引数を二重に渡すとエラーです。普通の位置引数をキーワード引数の後へ置くこともできません。複数のオプションがある関数では、名前を使うと意味が読み取りやすくなります。

単独の*より後の仮引数はキーワード専用です。例のcurrencyは名前を明示して渡すため、呼び出し箇所で金額の単位を確認できます。

デフォルト値の共有と呼び出し引数は、可変長引数と*・**による展開を使うで扱う規則とつながります。

キーワード専用引数と新しいリストを試す

arguments.pyへ全体コードを保存し、仮想環境でpython arguments.pyを実行します。

def format_expense(name, amount, *, currency="KRW"):
    return f"{name}: {amount:,} {currency}"

def append_name(name, names=None):
    if names is None:
        names = []
    names.append(name)
    return names

print(format_expense("lunch", 8000))
print(format_expense(amount=9000, name="dinner", currency="KRW"))
print(append_name("A"))
print(append_name("B"))

最後の二つが独立したリストになることに注目します。

lunch: 8,000 KRW
dinner: 9,000 KRW
['A']
['B']

既定値が作られるタイミングを理解する

既定値の式は関数定義が実行されたときに評価され、呼び出すたびに作り直されません。def append_name(name, names=[])では、一つのリストを複数の呼び出しで使うことになります。Noneを受けて本体でリストを作れば、必要なときに新しく用意できます。

明示的に渡した空リストを使い続けたいならif names is Noneで調べます。if not namesでは、その有効な空リストまで別のものへ置き換え、呼び出し側が意図した共有を変えてしまいます。

インターフェースと変更の契約を確認する

currencyを三番目の位置引数として渡し、キーワード専用の規則で拒否されることを確認します。別にnames = []を作りappend_name("A", names)を呼ぶと、今回は呼び出し側のリストを意図的に変更します。渡した可変オブジェクトの変更と、既定値の共有は別の論点です。

現在日時を既定値の式で取得した場合も、定義時の値が残ることがあります。毎回新しい日時が必要ならNoneを受けて本体で計算します。None自体を意味のある入力として受け付けるなら、別の専用値を用意して区別します。スコープと可変オブジェクトで、代入とオブジェクト変更の違いを確認できます。

デフォルト引数は呼び出しごとではなく、関数定義が実行されたときに評価されます。同じファイルで続けて呼ぶと、最初の関数はAを残したままBを追加し、Noneを使う版は呼び出しごとに新しいリストを作ります。

全体を defaults.py に保存し、同じ練習フォルダーで python defaults.py を実行します。

def shared(name, names=[]):
    names.append(name)
    return names
def fresh(name, names=None):
    if names is None:
        names = []
    names.append(name)
    return names
print(shared("A"))
print(shared("B"))
print(fresh("A"))
print(fresh("B"))

予想結果:

['A']
['A', 'B']
['A']
['B']

関連する公式リファレンス