✳️

Keyword-only引数 — `*,`が何で、なぜ使うか

`def f(a, b, *, c)`のあのアスタリスク

文法

def foo(a, b, *, c):
    ...

foo(1, 2, c=3)        # OK
foo(1, 2, 3)          # TypeError
foo(c=3, a=1, b=2)    # OK

*自体はマーカーで、値は受け取らない。「ここから先はキーワードのみ」という区切り。

*argsとの関係

*args(可変位置引数)も同じ効果を持つ。*argsの後ろのパラメータは自動的にkeyword-only。

def bar(a, *args, c):  # cはkeyword-only
    ...

なぜ使うか

1. デフォルトなしの引数をデフォルトの後ろに配置

def f(a=1, b=2, enable_evidence: bool):  # SyntaxError
    ...

def f(a=1, b=2, *, enable_evidence: bool):  # OK
    ...

2. 呼び出し意図の明確化

process(item, filter_list, True)
# Trueって何?

process(item, filter_list, enable_evidence=True)
# 自明

3. シグネチャ変更の安全性

パラメータ順序を変えても呼び出し側が壊れない。

標準ライブラリの例

sorted(iterable, *, key=None, reverse=False)
print(*objects, sep=' ', end='\n')

sorted(list, lambda x: x)がダメでsorted(list, key=lambda x: x)と書く必要があるのは、*で区切られているから。

Rubyとの比較

Rubyに*,のような文法はない。必要がないから。

def foo(a, b, c:)
  # ...
end

foo(1, 2, c: 3)   # OK
foo(1, 2, 3)      # ArgumentError

Rubyのc:は最初からキーワード専用。位置では渡せない。位置引数(a, b)とキーワード引数(c:)が文法的に分離されている。

Python Ruby
通常の引数 位置とキーワード両方可 位置のみ
キーワード専用 *,の後ろに宣言 name:形式で宣言
可変位置 *args *args
可変キーワード **kwargs **kwargs

Rubyは「キーワードで受けたいなら最初からキーワードで宣言しろ」という立場。Pythonは「同じ引数でも呼び出し方を強制できる」という立場。

キーポイント

1

`*,`の後ろのパラメータはキーワードのみ — 位置で渡すとTypeError

2

`*`は値を受け取らないマーカー — `*args`の後ろも同じくkeyword-only

3

デフォルト引数の後ろに非デフォルト引数を置く唯一の方法

4

呼び出し側に意図を強制 — `True`のような意味不明な位置引数を防ぐ

5

Rubyの`name:`自体がkeyword-onlyなので同等の文法は不要

ユースケース

フラグ引数(boolオプション)を受ける関数 — `enable_xxx=True`を強制 パラメータが5個超える関数 — 位置呼び出しを防ぎキーワードのみ許可 APIエントリポイント — シグネチャ変更時に呼び出し側が壊れないことを保証