- Python
Python *argsと**kwargsの使い方|可変長引数とデフォルト引数
Pythonの *args は「余った位置引数をタプルにまとめて受け取る」仮引数、**kwargs は「余ったキーワード引数を辞書にまとめて受け取る」仮引数です。 この2つを使うと、引数の個数が決まっていない関数(可変長引数の関数)を書けます。args・kwargs という名前は慣習で、意味を持つのは * と ** の記号です。
この記事では、関数定義で使える引数の5種類(位置専用・通常・*args・キーワード専用・**kwargs)を 並べる順番の図と表で整理し、すべてのコードを Python 3.14.7 で実行した結果と一緒に載せています(3.11.15 でも同じコードを流し、違いがあった箇所は明記しています)。呼び出し側の *リスト・**辞書 によるアンパック、TypeError の代表パターンと直し方、デフォルト引数にリストを置いたときの挙動、型ヒントの書き方、デコレータでの引数転送の定型、functools.partial まで順に見ていきます。
*argsと**kwargsの早見表
| やりたいこと | 書き方 | 関数の中での型・ポイント |
|---|---|---|
| 位置引数を何個でも受け取る | def f(*args): |
tuple。0個なら () |
| キーワード引数を何個でも受け取る | def f(**kwargs): |
dict。0個なら {} |
| 両方受け取る | def f(*args, **kwargs): |
*args が先、**kwargs が最後 |
| 必須の引数+残り | def f(first, *rest): |
1つ目だけ名前で受ける |
| キーワードでしか渡せない引数 | def f(*, debug=False): |
* より後ろはキーワード専用 |
| 位置でしか渡せない引数 | def f(x, y, /): |
/ より前は位置専用 |
| リストを展開して渡す | f(*items) |
呼び出し側の * はアンパック |
| 辞書を展開して渡す | f(**options) |
キーが引数名になる |
| 受け取った引数をそのまま別の関数へ | g(*args, **kwargs) |
デコレータ・継承の定型 |
| 既定値を持たせる | def f(x, size=10): |
既定値は定義時に1回だけ評価される |
| 既定値を空リストにしたい | def f(items=None): + if items is None: items = [] |
items=[] は呼び出し間で共有される |
| 一部の引数を固定した関数を作る | functools.partial(f, exp=2) |
引数を並べる順番は次のとおりです。
def f(位置専用, /, 通常, *args, キーワード専用, **kwargs):
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Python *argsと**kwargsの使い方|可変長引数とデフォルト引数」
*argsの使い方:位置引数をタプルで受け取る
前提:Python 3.8 以降(位置専用引数 / を使う例があるため)。この記事の実行結果は Python 3.14.7 のものです。
def show(*args, **kwargs):
print("args =", args, type(args).__name__)
print("kwargs=", kwargs, type(kwargs).__name__)
show(1, 2, 3, name="pen", price=150)
show()
args = (1, 2, 3) tuple
kwargs= {'name': 'pen', 'price': 150} dict
args = () tuple
kwargs= {} dict
*args はタプル、**kwargs は辞書です。 何も渡さなければ空のタプルと空の辞書になるので、None かどうかを調べる必要はありません。
*args だけを使った合計の関数です。名前は args でなくても構いません。中身がわかる名前にすると読みやすくなります。
def total(*prices):
return sum(prices)
print(total(), total(100), total(100, 200, 300))
0 100 600
**kwargsの使い方:キーワード引数を辞書で受け取る
**kwargs は、仮引数に名前のないキーワード引数をまとめて辞書にします。HTML タグの属性のように、どんな名前が来るか事前に決められないときに向いています。
def make_tag(name, **attrs):
text = "".join(f' {k}="{v}"' for k, v in attrs.items())
return f"<{name}{text}>"
print(make_tag("a", href="/items", target="_blank"))
<a href="/items" target="_blank">
辞書なので、attrs.get("href")、"target" in attrs、attrs.items() といった辞書の操作がそのまま使えます。渡した順番も保たれます。
| 比較 | *args |
**kwargs |
|---|---|---|
| 受け取るもの | 余った位置引数 | 余ったキーワード引数 |
| 関数の中での型 | tuple |
dict(キーは str) |
| 何も来ないとき | () |
{} |
| 1つの関数に書ける数 | 1つ | 1つ |
| 置く場所 | 通常の引数の後 | 必ずいちばん最後 |
| 向いている場面 | 同じ種類の値を何個でも(合計、連結、パスの結合) | 名前つきのオプションを何個でも(属性、設定、別の関数への転送) |
引数5種類の順番(図と表)
Python の関数定義では、引数を5つの種類に分けられます。並べる順番は決まっていて、入れ替えると SyntaxError になります。
flowchart LR
A["1. 位置専用<br>a, b"] --> S1["/"]
S1 --> B["2. 通常<br>c, d=10"]
B --> C["3. *args<br>または * だけ"]
C --> D["4. キーワード専用<br>e, g=20"]
D --> E["5. **kwargs"]
| 順番 | 種類 | 書き方 | 位置で渡す | キーワードで渡す | inspect での名前 |
|---|---|---|---|---|---|
| 1 | 位置専用引数 | / より前 |
できる | できない | POSITIONAL_ONLY |
| 2 | 通常の引数 | / と * の間 |
できる | できる | POSITIONAL_OR_KEYWORD |
| 3 | 可変長の位置引数 | *args |
余りをまとめて受ける | — | VAR_POSITIONAL |
| 4 | キーワード専用引数 | *args または * より後 |
できない | できる | KEYWORD_ONLY |
| 5 | 可変長のキーワード引数 | **kwargs |
— | 余りをまとめて受ける | VAR_KEYWORD |
5種類すべてを使った関数で確かめます。
import inspect
def f(a, b, /, c, d=10, *args, e, g=20, **kwargs):
print(f"a={a} b={b} c={c} d={d} args={args} e={e} g={g} kwargs={kwargs}")
f(1, 2, 3, e=5)
f(1, 2, c=3, e=5)
f(1, 2, 3, 4, 5, 6, e=7, g=8, h=9)
for p in inspect.signature(f).parameters.values():
print(f"{p.name:7} {p.kind.name}")
a=1 b=2 c=3 d=10 args=() e=5 g=20 kwargs={}
a=1 b=2 c=3 d=10 args=() e=5 g=20 kwargs={}
a=1 b=2 c=3 d=4 args=(5, 6) e=7 g=8 kwargs={'h': 9}
a POSITIONAL_ONLY
b POSITIONAL_ONLY
c POSITIONAL_OR_KEYWORD
d POSITIONAL_OR_KEYWORD
args VAR_POSITIONAL
e KEYWORD_ONLY
g KEYWORD_ONLY
kwargs VAR_KEYWORD
3つ目の呼び出しでは、位置引数6個のうち4個が a, b, c, d に入り、残りの 5, 6 が args に、名前の決まっていない h=9 が kwargs に入っています。
順番についての細かいルール
| ルール | 例 | 補足 |
|---|---|---|
| 既定値つきの引数は、既定値なしの引数より後ろ | def f(a, b=1): |
位置専用・通常の引数の範囲でのルール |
| キーワード専用引数は、既定値の有無を自由に並べられる | def f(*, e, g=20, h): |
名前で渡すので順番に依存しない |
*args が要らないときは * だけ書く |
def f(a, *, debug=False): |
* より後ろがキーワード専用になる |
**kwargs の後ろには何も書けない |
def f(**kwargs): |
必ず最後 |
/ と *args はそれぞれ1回だけ |
順番を入れ替えたときのエラーメッセージを、compile() で確かめました(Python 3.14.7)。
| 書いた定義 | SyntaxError のメッセージ |
|---|---|
def g(a=1, b): pass |
parameter without a default follows parameter with a default |
def g(**kwargs, a): pass |
arguments cannot follow var-keyword argument |
def g(*args, a, *more): pass |
* argument may appear only once |
def g(*, **kwargs): pass |
named arguments must follow bare * |
def g(a, /, b, /): pass |
/ may appear only once |
def g(*args=()): pass |
var-positional argument cannot have default value |
1行目のメッセージは、Python 3.11.15 では non-default argument follows default argument でした。表現は違いますが、同じ内容です。
キーワード専用引数(*)を使う場面
意味が伝わりにくい引数は、* の後ろに置いてキーワード専用にすると、呼び出し側が読みやすくなります。
def export(path, *, overwrite=False, encoding="utf-8"):
...
export("out.csv", overwrite=True) # 何を True にしたのかが読める
# export("out.csv", True) # TypeError になるので、この書き方は入り込まない
True/False や数値が位置で並ぶと、どれが何の指定なのかを関数定義まで見に行くことになります。キーワード専用にしておくと、名前つきで書くことが関数の側から決まります。
位置専用引数(/)を使う場面
引数の名前を呼び出し側に公開したくないときに / を使います。 あとで引数名を変えても、呼び出し側のコードに影響しません。また、**kwargs と同じ名前がぶつかるのを避けられます。
def with_kwargs(x, /, **kwargs):
return x, kwargs
print(with_kwargs(1, x=2))
(1, {'x': 2})
x は位置専用なので、x=2 は kwargs 側に入ります。/ がなければ「x に値が2回渡された」という TypeError になる場面です。
呼び出し側のアンパック(*リストと**辞書)
呼び出すときの * と ** は、定義のときとは逆に「まとまりをばらして渡す」働きをします。 これをアンパックと呼びます。
def order(item, qty, price):
return f"{item} x{qty} = {qty*price}"
row = ["ノート", 3, 300]
opt = {"item": "ペン", "qty": 2, "price": 150}
print(order(*row)) # order("ノート", 3, 300) と同じ
print(order(**opt)) # order(item="ペン", qty=2, price=150) と同じ
print(order("付箋", *[5, 120])) # 一部だけ展開
print(order(*("消しゴム",), **{"qty": 1, "price": 80}))
ノート x3 = 900
ペン x2 = 300
付箋 x5 = 600
消しゴム x1 = 80
| 場所 | * の意味 |
** の意味 |
|---|---|---|
関数定義 def f(*args, **kwargs) |
余った位置引数をタプルにまとめる | 余ったキーワード引数を辞書にまとめる |
関数呼び出し f(*seq, **mapping) |
リストやタプルを位置引数にばらす | 辞書をキーワード引数にばらす |
* と ** は、関数呼び出し以外でも同じ考え方で使えます。
a = [1, 2]; b = [3]
print([*a, *b], {**{"x": 1}, **{"x": 2, "y": 3}}) # リスト・辞書の結合
first, *rest = [1, 2, 3, 4] # 代入の左辺でまとめる
print(first, rest)
print(*"abc", sep="-") # 文字列も展開できる
[1, 2, 3] {'x': 2, 'y': 3}
1 [2, 3, 4]
a-b-c
辞書の結合では、同じキーがあると後ろの値が残ります(x は 2)。
呼び出し側にも順番のルールがあります。
| 書いた呼び出し | SyntaxError のメッセージ(3.14.7) | 直し方 |
|---|---|---|
f(a=1, 2) |
positional argument follows keyword argument | 位置引数を先に書く |
f(**{'a':1}, 2) |
positional argument follows keyword argument unpacking | 位置引数を先に書く |
f(**{'a':1}, *[2]) |
iterable argument unpacking follows keyword argument unpacking | * を ** より先に書く |
TypeErrorの代表パターンと直し方
引数まわりのエラーは、メッセージの形を覚えておくと原因がすぐにわかります。次の関数を使って再現します。
def two(a, b): return a + b
def greet(name, greeting="こんにちは"): return f"{greeting}、{name}"
def pos_only(x, y, /): return x + y
def kw_only(*, debug): return debug
| 呼び出し | TypeError のメッセージ(Python 3.14.7 の実行結果) | 原因と直し方 |
|---|---|---|
two(1) |
two() missing 1 required positional argument: 'b' | 必須の引数が足りない。渡すか、既定値を付ける |
two() |
two() missing 2 required positional arguments: 'a' and 'b' | 同上 |
two(1, 2, 3) |
two() takes 2 positional arguments but 3 were given | 位置引数が多い。余りを受けたいなら *args |
greet("田中", name="佐藤") |
greet() got multiple values for argument 'name' | 同じ引数に位置とキーワードの両方で渡している |
two(1, 2, **{"a": 9}) |
two() got multiple values for argument 'a' | 展開した辞書のキーが位置引数と重なっている |
greet("田中", greting="やあ") |
greet() got an unexpected keyword argument 'greting'. Did you mean 'greeting'? | 引数名のつづり。3.11.15 では「Did you mean」の部分は出ない |
pos_only(x=1, y=2) |
pos_only() got some positional-only arguments passed as keyword arguments: 'x, y' | / より前はキーワードで渡せない。pos_only(1, 2) と書く |
kw_only(True) |
kw_only() takes 0 positional arguments but 1 was given | * より後ろは位置で渡せない。kw_only(debug=True) と書く |
kw_only() |
kw_only() missing 1 required keyword-only argument: 'debug' | 既定値のないキーワード専用引数は必須 |
greet(**{"name": "a"}, **{"name": "b"}) |
greet() got multiple values for keyword argument 'name' | 展開する辞書どうしでキーが重なっている |
greet(**{1: "a"}) |
keywords must be strings | ** で展開する辞書のキーは文字列 |
two(*5) |
two() argument after * must be an iterable, not int |
* の後ろはリストやタプルなどの反復可能オブジェクト |
two(**[1, 2]) |
two() argument after ** must be a mapping, not list |
** の後ろは辞書 |
特に見かけることが多いのは、次の3つです。
- missing … required positional argument:メソッド定義で
selfを書き忘れたとき、クラスをインスタンス化せずにメソッドを呼んだときにも出ます。 - got multiple values for argument:第1引数を位置で渡しつつ、同じ名前をキーワードや
**辞書でも渡したときに出ます。辞書を展開する前にopts.pop("name", None)で取り除くか、位置で渡すのをやめます。 - got an unexpected keyword argument:つづりの違いのほか、ライブラリのバージョンによって引数名が変わったときにも出ます。
help(関数)やinspect.signature(関数)で受け付ける引数を確認できます。
**kwargs を持つ関数は、どんな名前のキーワード引数でも受け取るので、つづりを間違えても TypeError になりません。
def search(**kwargs): return kwargs
print(search(keyword='pen', limt=5)) # limit のつもり
{'keyword': 'pen', 'limt': 5}
決まった名前のオプションしか受け付けないなら、**kwargs ではなくキーワード専用引数として1つずつ書くと、間違いを呼び出しの時点で見つけられます。後述の TypedDict と Unpack を使う方法もあります。
デフォルト引数の使い方と、リストを置いたときの挙動
デフォルト引数は、呼び出し側が省略したときに使われる値です。 def greet(name, greeting="こんにちは") のように = で書きます。
デフォルト値は関数を定義したときに1回だけ評価される
def 文が実行された時点で既定値の式が計算され、その結果が関数オブジェクトに保存されます。呼び出しのたびに計算し直されるわけではありません。
def mk():
print(" default evaluated"); return 0
print("before def")
def h(v=mk()): return v
print("after def"); h(); h()
n = 1
def use_n(v=n): return v
n = 99; print(use_n())
before def
default evaluated
after def
1
default evaluated は def の時点で1回だけ表示され、h() を2回呼んでも表示されません。また、use_n の既定値は定義時の n(1)で、あとから n = 99 にしても変わりません。
直す前:デフォルト引数に空リストを置く
def add_item(item, items=[]):
items.append(item); return items
print(add_item("a"))
print(add_item("b"))
print(add_item("c", []))
print(add_item("d"))
print(add_item.__defaults__)
['a']
['a', 'b']
['c']
['a', 'b', 'd']
(['a', 'b', 'd'],)
2回目の呼び出しで ['b'] ではなく ['a', 'b'] が返っています。 既定値のリストは定義時に1つだけ作られ、省略して呼ぶたびに同じリストが使われるためです。add_item.__defaults__ を見ると、関数が持っている既定値そのものが書き換わっていることがわかります。辞書({})や集合(set())でも同じことが起きます。
直した後:Noneを既定値にして、関数の中で作る
def add_item2(item, items=None):
if items is None:
items = []
items.append(item); return items
print(add_item2("a"))
print(add_item2("b"))
['a']
['b']
呼び出しのたびに新しいリストが作られるので、前回の結果が残りません。
同じ考え方は「現在時刻」にも当てはまります。
import datetime, time
def now_bad(ts=datetime.datetime.now()): return ts
x1 = now_bad(); time.sleep(0.05); x2 = now_bad(); print(x1 == x2)
def now_ok(ts=None): return ts if ts is not None else datetime.datetime.now()
y1 = now_ok(); time.sleep(0.05); y2 = now_ok(); print(y1 == y2)
True
False
now_bad は、何度呼んでも関数を定義した時刻を返します。None を既定値にして中で now() を呼ぶと、呼び出しごとの時刻になります。
| 既定値にしたいもの | 書き方 | 理由 |
|---|---|---|
数値・文字列・True/False・None・タプル |
そのまま書く(size=10) |
変更できない値なので共有されても影響がない |
| 空のリスト・辞書・集合 | =None にして関数の中で作る |
定義時に作った1つを全呼び出しで共有するため |
| 現在時刻・乱数・連番 | =None にして関数の中で求める |
定義時の値に固定されるため |
| 「省略」と「None を渡した」を区別したい | 専用の目印オブジェクト(下の例) | None 自体に意味がある場合 |
None も有効な値として渡したいときは、目印用のオブジェクトを既定値にします。
_MISSING = object()
def update(name, note=_MISSING):
if note is _MISSING:
return f"{name}: noteは変更なし"
return f"{name}: note={note!r}"
print(update("a"))
print(update("a", None))
a: noteは変更なし
a: note=None
*argsと**kwargsの型ヒント
*args: int は「args の要素1つ1つが int」という意味で、args 全体の型は tuple[int, ...] になります。 **kwargs: str は「値が str」で、全体は dict[str, str] です。*args: tuple[int, ...] と書くと「タプルを何個でも受け取る」という別の意味になります。
from typing import TypedDict, Unpack, NotRequired
def total(*args: int) -> int:
return sum(args)
def labels(**kwargs: str) -> list[str]:
return [f"{k}={v}" for k, v in kwargs.items()]
class SearchOptions(TypedDict):
keyword: str
limit: NotRequired[int]
desc: NotRequired[bool]
def search(**kwargs: Unpack[SearchOptions]) -> str:
limit = kwargs.get("limit", 10)
return f"{kwargs['keyword']} limit={limit} desc={kwargs.get('desc', False)}"
print(search(keyword="pen", limit=5))
pen limit=5 desc=False
型チェッカー mypy 1.20.2 で reveal_type() を使って確かめると、args は tuple[int, ...]、kwargs は dict[str, str] と表示されました。
キーごとに型が違うときは TypedDict と Unpack
**kwargs: str は、すべての値が同じ型のときにしか使えません。キーごとに型が違う場合は、TypedDict で名前と型を定義し、**kwargs: Unpack[SearchOptions] と書きます(PEP 692。Python 3.12 で正式に導入されました。typing.Unpack と NotRequired は 3.11 にもあり、上のコードは 3.11.15 でも実行できました)。
間違った呼び出しを mypy 1.20.2 にかけた結果です。
| 呼び出し | mypy のメッセージ |
|---|---|
total(1, "2") |
Argument 2 to "total" has incompatible type "str"; expected "int" |
labels(a=1) |
Argument "a" to "labels" has incompatible type "int"; expected "str" |
search(keyword="pen", limt=5) |
Unexpected keyword argument "limt" for "search"; did you mean "limit"? |
search(limit=5) |
Missing named argument "keyword" for "search" |
search(keyword="pen", limit="5") |
Argument "limit" to "search" has incompatible type "str"; expected "int" |
**kwargs のままでも、つづりの違い・必須キーの不足・型の違いをコードを動かす前に見つけられます。 実行時には型ヒントによる検査は行われないので、型チェッカーやエディタの補完と組み合わせて使います。
| 書き方 | 意味 | 向いている場面 |
|---|---|---|
*args: int |
位置引数はすべて int | 同じ型の値を何個でも |
**kwargs: str |
キーワード引数の値はすべて str | 同じ型のオプション |
**kwargs: Unpack[TD] |
TypedDict で決めたキーと型だけ | 名前と型が決まっているオプションを転送する |
*args: Any, **kwargs: Any |
何でも受け取る | 型を問わずに転送するだけの関数 |
*args: P.args, **kwargs: P.kwargs |
元の関数と同じ引数 | デコレータ(次の節) |
型ヒント全般の書き方は、既存記事「Pythonの型ヒントの書き方」で扱っています。
デコレータでの引数転送の定型
*args, **kwargs がもっともよく使われるのは、受け取った引数をそのまま別の関数へ渡す場面です。デコレータ(関数に前後の処理を足す仕組み)では、次の形が定型になります。
import functools, inspect
def log_calls(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"call {func.__name__} args={args} kwargs={kwargs}")
result = func(*args, **kwargs)
print(f" -> {result!r}")
return result
return wrapper
@log_calls
def area(width, height=1, *, unit="cm"):
"""面積を返す"""
return f"{width*height}{unit}2"
area(3, 4)
area(5, unit="m")
print(area.__name__, area.__doc__, inspect.signature(area))
call area args=(3, 4) kwargs={}
-> '12cm2'
call area args=(5,) kwargs={'unit': 'm'}
-> '5m2'
area 面積を返す (width, height=1, *, unit='cm')
ポイントは3つです。
wrapper(*args, **kwargs)でまとめて受け取り、func(*args, **kwargs)でばらして渡す。 元の関数の引数がどんな形でも、そのまま通せます。@functools.wraps(func)を付けると、関数名・docstring・シグネチャが元の関数のものとして見えます。- 型ヒントを付けるときは
ParamSpecを使うと、デコレータを通しても引数の型が保たれます。
from typing import Callable, ParamSpec, TypeVar
import functools
P = ParamSpec("P")
R = TypeVar("R")
def log_calls(func: Callable[P, R]) -> Callable[P, R]:
@functools.wraps(func)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
print(f"call {func.__name__}")
return func(*args, **kwargs)
return wrapper
この形で area("3") や area(3, unit=1) を mypy 1.20.2 にかけると、Argument 1 to "area" has incompatible type "str"; expected "int"、Argument "unit" to "area" has incompatible type "int"; expected "str" と、元の関数の型に沿って検出されました(area の引数に int・str の型ヒントを付けた場合)。
継承でも同じ形が使えます。子クラスで追加の引数だけを取り出し、残りを親クラスへ渡します。
class Base:
def __init__(self, name, *, active=True):
self.name, self.active = name, active
class Member(Base):
def __init__(self, *args, rank="regular", **kwargs):
super().__init__(*args, **kwargs)
self.rank = rank
m = Member("田中", active=False, rank="gold")
print(m.name, m.active, m.rank)
田中 False gold
デコレータの作り方やレシピは、既存記事「Pythonデコレータの使い方」にまとめています。
functools.partialで引数の一部を固定する
functools.partial は、関数の引数の一部をあらかじめ埋めた新しい関数を作ります。 「引数を受け取って転送する」*args, **kwargs と対になる道具です。
from functools import partial
def power(base, exp): return base ** exp
square = partial(power, exp=2)
print(square(5), square.func.__name__, square.args, square.keywords)
print(square(5, exp=3)) # キーワードで固定した値は上書きできる
two_pow = partial(power, 2) # 位置で固定すると先頭の引数が埋まる
print(two_pow(10))
print(list(map(partial(int, base=2), ["101", "111"])))
p = partial(print, sep=" | ", end=" <\n")
p("a", "b")
25 power () {'exp': 2}
125
1024
[5, 7]
a | b <
| 固定のしかた | 例 | 呼び出し時の動き |
|---|---|---|
| キーワードで固定 | partial(power, exp=2) |
呼び出し時に同じ名前を渡すと上書きされる |
| 位置で固定 | partial(power, 2) |
先頭から埋まり、呼び出し時の位置引数はその後ろに続く |
位置で固定した引数を、呼び出し時にキーワードでもう一度渡すと TypeError になります。partial(power, 2)(base=3) の実行結果は power() got multiple values for argument 'base' でした。
partial は、map や sorted の key、コールバックのように「引数の数が決まっている場所へ関数を渡す」場面で使いやすくなります。lambda x: power(x, 2) と書いても同じ結果になりますが、partial で作った関数は .func・.args・.keywords で中身を確認できます。
*argsと**kwargsを使うかどうかの判断表
| 状況 | 向いている書き方 | 理由 |
|---|---|---|
| 同じ種類の値を何個でも受けたい | *args |
total(1, 2, 3) のように自然に呼べる |
| すでにリストになっている値を受けたい | リストを1つの引数で受ける(def total(prices):) |
呼び出し側で * を付ける手間がない |
| 名前が事前に決まらないオプション | **kwargs |
属性・ラベルなど |
| 名前が決まっているオプション | キーワード専用引数(*, limit=10, desc=False) |
つづりの違いが TypeError になり、補完も効く |
| 別の関数へそのまま渡す | *args, **kwargs で受けて同じ形で渡す |
デコレータ・継承・ラッパー |
| True/False や数値のオプション | * の後ろに置く |
呼び出し側に名前が残る |
| 引数名を将来変えるかもしれない | / の前に置く |
呼び出し側が名前に依存しない |
よくある質問
Q. args と kwargs という名前でなければいけませんか?
A. 名前は自由です。意味を持つのは * と ** で、この記事の *prices や **attrs のように中身を表す名前にできます。そのまま別の関数へ渡すだけの場合は、慣習どおり *args, **kwargs と書くと、読む人に意図が伝わりやすくなります。
Q. *args の後ろに普通の引数を書くとどうなりますか?
A. キーワード専用引数になります。def f(*args, sep="-") の sep は、f(1, 2, sep="/") のように名前を付けたときだけ渡せます。位置で渡した値はすべて args に入ります。
Q. **kwargs で受け取った辞書を書き換えると、呼び出し元の辞書も変わりますか?
A. 変わりません。f(**opt) と呼んだとき、関数の中の kwargs は新しく作られた辞書です。kwargs["x"] = 1 と追加しても、kwargs.pop("x") と取り除いても、元の opt はそのままです。ただし、値として入っているリストなどのオブジェクト自体は共有されています。
Q. デフォルト引数に空リストを書きたいときはどうすればよいですか?
A. items=None と書き、関数の先頭で if items is None: items = [] とします。この記事の実行結果のとおり、items=[] と書くと定義時に作られた1つのリストが呼び出しのたびに使われ、前回の値が残ります。
まとめ
*argsは余った位置引数をタプルで、**kwargsは余ったキーワード引数を辞書で受け取る。名前は自由で、記号に意味がある- 引数の順番は「位置専用
/→ 通常 →*args→ キーワード専用 →**kwargs」。入れ替えると SyntaxError になる - 呼び出し側の
*リスト・**辞書はアンパック。定義側の「まとめる」と向きが逆 - TypeError はメッセージの形で原因がわかる。missing、multiple values、unexpected keyword、positional-only の4つを押さえる
- デフォルト値は定義時に1回だけ評価される。リスト・辞書・現在時刻は
Noneを既定値にして関数の中で作る - 型ヒントは
*args: int(要素の型)と**kwargs: Unpack[TypedDict]。デコレータはParamSpecで元の引数の型を保てる - 受け取って転送するなら
*args, **kwargs、一部を固定するならfunctools.partial
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Python *argsと**kwargsの使い方|可変長引数とデフォルト引数」
株式会社RJCでは、Pythonの引数の仕組みのような基礎を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。