Python の仮引数 5 種類について記します。
手元の関数の仮引数の種類がどれか表示する関数 inspect_params() も載せています (inspect.signature() を呼んでいるだけです)。
参考文献 (リンク下のメモは筆者による)
-
用語集 — Python 3.13.11 ドキュメント#引数 (argument)
- 関数を呼ぶときに渡す値 (実引数) は、渡し方が 2 種類あります (位置で渡すか、キーワードで渡す)。どちらかの渡し方で渡します。一旦キーワードで渡したら、続く実引数もキーワードでしか渡せません。
-
用語集 — Python 3.13.11 ドキュメント#parameter
- 関数が受け取る値の定義 (仮引数) は、「そこにどんな渡し方で何個の値を渡せるか」によって 5 種類あります。
- 位置でもキーワードでも渡せて、1 個の値を渡せる (POSITIONAL_OR_KEYWORD)
- 位置によってのみ渡せて、1 個の値を渡せる (POSITIONAL_ONLY)
- キーワードによってのみ渡せて、1 個の値を渡せる (KEYWORD_ONLY)
- 位置によってのみ渡せて、任意個数の値を渡せる (VAR_POSITIONAL)
- キーワードによってのみ渡せて、任意個数の値を渡せる (VAR_KEYWORD)
- 関数が受け取る値の定義 (仮引数) は、「そこにどんな渡し方で何個の値を渡せるか」によって 5 種類あります。
- inspect --- 活動中のオブジェクトを調査する — Python 3.13.11 ドキュメント#signature
-
Pythonの位置専用引数とキーワード専用引数についてまとめてみた #Python - Qiita
- 仮引数の種類の記事で、利点や標準モジュールでの使用例があります。
make_curry(20, 100) # 1 番目の 20 と、2 番目の 100 を渡す
make_curry(20, meat=100) # 1 番目の 20 と、キーワード meat の 100 を渡す
make_curry(roux=20, meat=100) # キーワード roux の 20 と、キーワード meat の 100 を渡す
make_curry(meat=100, roux=20) # キーワード meat の 100 と、キーワード roux の 20 を渡す
すべての仮引数が位置でもキーワードでも渡せるケース
オブジェクトを調査してくれる標準モジュール inspect の inspect.signature() で、ルーと肉と油を受け取る関数 make_curry() のシグネチャ (仮引数情報) を調べます。
import inspect
def inspect_params(func):
sig = inspect.signature(func)
print('|name|default|kind|')
print('|:---|:------|:---|')
for p in sig.parameters.values():
default = '-' if p.default is inspect._empty else p.default
print(f'|{p.name}|{default}|{p.kind}|')
def make_curry(roux, meat, oil=10):
total = roux + meat + oil
print(f'{roux=}, {meat=}, {oil=}, {total=}')
inspect_params(make_curry)
調査結果は以下のようになります。
| name | default | kind |
|---|---|---|
| roux | - | POSITIONAL_OR_KEYWORD |
| meat | - | POSITIONAL_OR_KEYWORD |
| oil | 10 | POSITIONAL_OR_KEYWORD |
ルーも肉も油も、位置でもキーワードでも渡せる種類の仮引数です。
例えば肉は、「2 番目の実引数」としても「キーワード meat の実引数」としても渡せます。
したがって、以下のどの呼び出しも有効で同じ結果になります。
make_curry(20, 100) # ルーも肉も位置で渡せます
make_curry(20, meat=100) # ルーのみ位置でも渡せます (肉は 2 番目なので肉のみ位置で渡すのは不可)
make_curry(roux=20, meat=100) # ルーも肉もキーワードでも渡せます
make_curry(meat=100, roux=20) # キーワードで渡すなら実引数の順番は問いません
# --> roux=20, meat=100, oil=10, total=130
位置でのみ渡せる仮引数があるケース
次に、make_curry() のルーを位置でのみ渡せるようにします。
カレーにルーは必要なので、最初の引数を必ずルーにする決まりにしてもよいと思います。
関数定義の引数リストに / を入れると、それより前の仮引数が位置でのみ渡せるようになります。
def make_curry(roux, /, meat, oil=10):
total = roux + meat + oil
print(f'{roux=}, {meat=}, {oil=}, {total=}')
| name | default | kind |
|---|---|---|
| roux | - | POSITIONAL_ONLY |
| meat | - | POSITIONAL_OR_KEYWORD |
| oil | 10 | POSITIONAL_OR_KEYWORD |
こうすると、カレーをつくるときのルーの渡し方がいつも統一される反面、柔軟な呼び出し方はできなくなります。
make_curry(20, 100) # どちらも位置で渡せます
# --> roux=20, meat=100, oil=10, total=130
make_curry(20, meat=100) # ルーのみ位置で渡せます (肉は 2 番目なので肉のみ位置で渡すのは不可)
# --> roux=20, meat=100, oil=10, total=130
# make_curry(roux=20, meat=100) # もはやルーをキーワードで渡すことはできません
# TypeError: make_curry() got some positional-only arguments passed as keyword arguments: 'roux'
キーワードでのみ渡せる仮引数があるケース
次に、最初のルーを除いた引数を逆にキーワードでのみ渡せるようにします。
make_curry(20, 100) と呼び出されているのを見た人が、「カレーだから最初の 20 はルーだろうけど、次の 100 は何を渡しているんだろう? たまねぎ?」となりかねないからです。キーワードを強制すれば、肉と間違っていちごジャムを渡す事故なども起きにくそうです。
関数定義の引数リストに * を入れると、それより後ろの仮引数がキーワードでのみ渡せるようになります。
def make_curry(roux, /, *, meat, oil=10):
total = roux + meat + oil
print(f'{roux=}, {meat=}, {oil=}, {total=}')
| name | default | kind |
|---|---|---|
| roux | - | POSITIONAL_ONLY |
| meat | - | KEYWORD_ONLY |
| oil | 10 | KEYWORD_ONLY |
こうするともはや肉を位置で渡すことはできなくなり、必ずキーワード meat= を付けて渡さなければならなくなります。
make_curry(20, meat=100)
# --> roux=20, meat=100, oil=10, total=130
# make_curry(20, 100) # もはや肉を位置で渡すことはできません
# TypeError: make_curry() takes 1 positional argument but 2 were given
位置でのみ渡せる可変長の仮引数があるケース
カレーに肉以外の具材も自由に入れられるようにしたいということもあると思います。
仮引数に * を付けると、「そこに位置によって何個でも値を渡せる」仮引数になります。
def make_curry(roux, /, meat, oil=10, *darkmatter):
total = roux + meat + oil + sum(darkmatter)
print(f'{roux=}, {meat=}, {oil=}, {sum(darkmatter)=}, {total=}')
| name | default | kind |
|---|---|---|
| roux | - | POSITIONAL_ONLY |
| meat | - | POSITIONAL_OR_KEYWORD |
| oil | 10 | POSITIONAL_OR_KEYWORD |
| darkmatter | - | VAR_POSITIONAL |
こうすると、以下のように他の具材もぽんぽんカレーに入れることができます。
ただし、他の具材を位置によって渡すため、他の値を渡すときはその手前にある油を必ず位置で渡す必要があります。また、他の具材をキーワードで渡すことはできません。
make_curry(20, 100, 10, 50, 80)
# --> roux=20, meat=100, oil=10, sum(darkmatter)=130, total=260
make_curry(20, 100, 10, *[50, 80])
# --> roux=20, meat=100, oil=10, sum(darkmatter)=130, total=260
# make_curry(20, 100, 10, onion=50, tomato=80) # ERROR
# make_curry(20, 100, 10, **{'onion': 50, 'tomato': 80}) # ERROR
キーワードでのみ渡せる可変長の仮引数があるケース
他の具材もぽんぽんカレーに入れられるようになったのはいいですが、いくらカレーが何でも合うとはいえ、何が入っているかわからないのは怖いということもあると思います。
そこで仮引数に ** を付けると、「そこにキーワードによって何個でも値を渡せる」仮引数になります。
def make_curry(roux, /, meat, oil=10, **ingredients):
total = roux + meat + oil + sum(ingredients.values())
print(f'{roux=}, {meat=}, {oil=}, {ingredients.keys()}={sum(ingredients.values())}, {total=}')
| name | default | kind |
|---|---|---|
| roux | - | POSITIONAL_ONLY |
| meat | - | POSITIONAL_OR_KEYWORD |
| oil | 10 | POSITIONAL_OR_KEYWORD |
| ingredients | - | VAR_KEYWORD |
こうすると、以下のように他の具材をキーワードによってぽんぽんカレーに入れることができます。キーワードなしに入れることはできません。
make_curry(20, 100, onion=50, tomato=80)
# --> roux=20, meat=100, oil=10, dict_keys(['onion', 'tomato'])=130, total=260
make_curry(20, 100, **{'onion': 50, 'tomato': 80})
# --> roux=20, meat=100, oil=10, dict_keys(['onion', 'tomato'])=130, total=260
make_curry(20, 100, **{'oil': 10, 'onion': 50, 'tomato': 80})
# --> roux=20, meat=100, oil=10, dict_keys(['onion', 'tomato'])=130, total=260
# make_curry(20, 100, 10, 50, 80) # ERROR
# make_curry(20, 100, 10, *[50, 80]) # ERROR