必须用args和kwargs时:当函数参数数量或名称不确定,如写装饰器、日志封装、参数透传等;否则易报TypeError。args收多余位置参数为tuple,kwargs收多余关键字参数为dict,二者共存时顺序固定。

什么时候必须用 *args 和 **kwargs?
当你写一个函数,但不确定调用方会传多少个位置参数、或者哪些关键字参数时,就得用它们。比如封装日志函数、装饰器、或做参数透传(如子类初始化时把父类不需要的参数全扔给父类),不加这两个就只能硬编码参数名,一改就报错。
常见错误现象:TypeError: my_func() got an unexpected keyword argument 'xxx',或者 my_func() takes 2 positional arguments but 5 were given——这往往不是调用错了,而是函数定义没预留扩展性。
-
*args把多余的位置参数收进一个tuple,顺序不变 -
**kwargs把多余的关键字参数收进一个dict,键是参数名,值是传入值 - 二者可以共存,但顺序固定:
def f(a, b, *args, c=10, **kwargs)——*args必须在普通参数之后、**kwargs之前;带默认值的 keyword-only 参数(如c)要放在*args后面
*args 和 **kwargs 怎么安全地转发给下游函数?
直接解包转发最常用,但要注意目标函数是否接受这些参数。盲目 func(*args, **kwargs) 很容易把不该传的参数塞进去,尤其当目标函数签名严格时。
使用场景:写一个通用重试装饰器,需要把原函数的所有参数原样传进去;或者子类 __init__ 中把未知参数交给父类处理。
- 转发前可先过滤:
filtered_kwargs = {k: v for k, v in kwargs.items() if k in ['timeout', 'retries']} - 用
inspect.signature()动态检查目标函数能收哪些参数(适合高级封装,但有性能开销) - 简单粗暴但有效:用
try/except TypeError捕获转发失败,再做降级处理(比如忽略不识别的**kwargs)
为什么 **kwargs 里不能有重复键?
因为 Python 关键字参数本身就不允许同名,**kwargs 只是把它收集起来,不会自动去重或合并。如果两个字典都含 'log_level',{**dict1, **dict2} 会以后者为准,但这是字典合并行为,不是 **kwargs 的语法限制。
容易踩的坑:
- 调用时写成
f(x=1, **{'x': 2})→ 直接报错:TypeError: f() got multiple values for argument 'x' -
**kwargs中的键必须是字符串,传{1: 'a'}会触发TypeError: keywords must be strings - 如果下游函数用
**kwargs接收,但内部又手动取kwargs.get('xxx'),记得处理None或缺省值,别假设一定存在
实际调试时怎么快速看清 *args 和 **kwargs 到底收到了什么?
别靠猜,加一行打印就行。但注意生产环境别留着,尤其 **kwargs 可能含敏感字段(如 password、token)。
示例:
def debug_func(*args, **kwargs):
print(f"args: {args}")
print(f"kwargs: {dict(kwargs)}") # 转 dict 是为了确保可读,避免某些自定义类的 __repr__ 干扰
return args, kwargs
更稳妥的做法是只打 key 名:
print(f"kwargs keys: {list(kwargs.keys())}")
复杂点在于嵌套调用链:A → B → C,每个都用 *args/**kwargs,这时单看某一层的打印可能看不出哪个参数在哪一层被消耗或改写。真遇到这种问题,得配合断点或 logging.debug 打标记,而不是依赖一次 print。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











