结论:*args 把多余的位置参数收集成元组,**kwargs 把多余的关键字参数收集成字典,让函数接受任意数量的参数。定义时的合法顺序:普通位置参数 → *args → 仅关键字参数 → **kwargs(默认值参数在 *args 之前)。
展开:解包是反向操作——调用时 f(*[1, 2]) 把序列摊开成位置参数,f(**{"a": 1}) 把字典摊开成关键字参数。几个进阶点:1)单独一个 * 后面的参数只能按关键字传(keyword-only),是 API 设计的防呆手段;2)Python 3.8 引入的 / 标记前面的参数只能按位置传(positional-only),如 dict.update;3)**kwargs 常用于装饰器和配置透传——包装函数不关心原函数签名,原样转发即可。易错点:*args 与关键字默认值参数混用时顺序写错会 SyntaxError;对同一个参数既传位置又传关键字(f(1, a=1))运行时报 TypeError。
def f(a, b=2, *args, c, **kwargs):
return a, b, args, c, kwargs
f(1, 3, 4, 5, c=6, d=7)
# (1, 3, (4, 5), 6, {'d': 7})
追问方向:inspect.signature 如何内省参数、functools.wraps 为何能保留被装饰函数的签名。