✳️
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エントリポイント — シグネチャ変更時に呼び出し側が壊れないことを保証