Python *args 和 **kwargs 到底是啥?7 个误用场景一次说清
系列:Python 底层设计逻辑|类型:排错清单型|分发:CSDN / 博客园 / 腾讯云
字数:约 1500 字
结论先行:*args 把多出来的位置参数收成一个元组,**kwargs 把多出来的关键字参数收成一个字典。它不是"任意参数"的魔法,而是"打包/解包"语法。* 解包可迭代对象,** 解包字典。用错的本质,是没分清"收集"和"展开"两个相反的方向。
1. 最基础的收集:位置参数进元组
def f(a, *args):
print(a, args)
f(1, 2, 3, 4) # 1 (2, 3, 4)
要点:args 是普通元组,a 先吃第一个,剩下全进 args。
2. 关键字参数进字典
def g(**kwargs):
print(kwargs)
g(name="Tom", age=18) # {'name': 'Tom', 'age': 18}
要点:kwargs 是普通 dict,调用时 k=v 形式都会被收进来。
3. 同时用:顺序铁律 必选 → *args → 默认 → **kwargs
def h(a, *args, b=0, **kwargs):
...
反例:def h(a, **kwargs, *args) 直接语法报错——**kwargs 必须放最后。
4. 反向用法:解包传参
def add(x, y, z):
return x + y + z
nums = (1, 2, 3)
print(add(*nums)) # 6 ← * 把元组拆成位置参数
d = {"x": 1, "y": 2, "z": 3}
print(add(**d)) # 6 ← ** 把字典拆成关键字参数
这是 *args/**kwargs 最高频的实战用法:把"已经装在容器里"的数据喂给函数。
5. 误用 1:用 args 当列表去改
def f(*args):
args.append(5) # AttributeError: 'tuple' object has no attribute 'append'
args 是元组,不可变。要改,先 list(args)。
6. 误用 2:字典解包键不匹配
def f(x, y): ...
f(**{"x": 1, "z": 2}) # TypeError: missing required argument 'y'
解包字典的键,必须和形参对得上,多一个少一个都报错。
7. 误用 3:和固定参数重名
def f(x, **kwargs):
f(x=1, x=2) # 调用端就语法错:重复关键字
别让 **kwargs 里出现和已有形参同名的键,否则冲突。
对照表:收集 vs 展开
| 写法 | 出现位置 | 作用 | 得到/消费 |
|---|---|---|---|
*args(def 内) |
形参 | 收集多余位置参数 | 元组 |
**kwargs(def 内) |
形参 | 收集多余关键字参数 | 字典 |
*seq(调用端) |
实参 | 展开可迭代为位置参数 | 多个位置参 |
**dict(调用端) |
实参 | 展开字典为关键字参数 | 多个关键字参 |
排错清单(收藏级)
- 顺序错:记住
位置 → *args → 默认 → **kwargs,**永远末尾。 - 想改收集结果:
args是元组,先list()再动。 - 解包字典报错:先核对键名与函数形参 100% 匹配。
- 装饰器里透传:
def wrapper(*args, **kwargs): return fn(*args, **kwargs)是标准写法,别手写成fn(args, kwargs)。 - 只想解包一部分:
first, *rest = seq也能用*做赋值解包。
一句话记住:def 里的 */** 是"收口袋",调用端的 */** 是"倒口袋"。方向搞反,就全乱了。

浙公网安备 33010602011771号