必须用args和kwargs的场景是无法预知调用方传参数量和名称时,如封装日志、代理调用、实现装饰器;二者需按“普通参数→args→默认参数→kwargs”顺序定义,args打包多余位置参数为元组,kwargs打包多余关键字参数为字典,转发时须用args和kwargs解包。

什么时候必须用 *args 和 **kwargs?
当你写一个函数,但无法提前确定调用方会传几个位置参数、或者哪些关键字参数时,就得靠它们。比如封装日志函数、代理调用其他函数、实现装饰器——这些场景里参数数量和名字都是动态的。
常见错误是硬编码参数名,结果一遇到新字段就报 TypeError: unexpected keyword argument;或者用列表/字典手动传参,反而让调用方写得更啰嗦。
-
*args捕获所有多余的位置参数,打包成tuple -
**kwargs捕获所有多余的关键字参数,打包成dict - 二者可以同时存在,但
*args必须写在**kwargs前面
*args 和 **kwargs 在函数定义里的位置有讲究吗?
有。Python 语法强制要求:普通参数 → *args → 默认参数 → **kwargs。顺序错会直接报 SyntaxError。
比如这个写法合法:
def log_event(level, *args, timestamp=True, **kwargs):而这样会报错:
def bad_func(*args, level, **kwargs): # SyntaxError
- 如果想让
level是必填参数,又允许后面跟任意位置参数,level必须放在*args前面 -
**kwargs必须是最后一个,否则它后面的参数永远无法被赋值 - 默认参数(如
timestamp=True)可以放在*args后、**kwargs前,它只对显式传入的关键词生效
怎么安全地把 *args 和 **kwargs 透传给下游函数?
直接写 func(*args, **kwargs) 就行,这是最常用也最可靠的转发方式。别试图手动解包或拼接参数列表——容易漏掉类型、破坏原意。
立即学习“Python免费学习笔记(深入)”;
典型陷阱是误用 func(args, kwargs)(少写了星号),结果把整个 tuple 和 dict 当成两个位置参数传进去,下游函数立刻崩溃。
- 转发时加星号是“解包”,不加是“整体传入”
- 如果下游函数不接受某些
**kwargs里的键,要提前过滤,比如:filtered_kwargs = {k: v for k, v in kwargs.items() if k not in ['debug', 'trace']} - 注意
*args解包后可能触发下游函数的类型检查(比如它期望字符串,但你传了数字),这属于业务逻辑问题,不是语法问题
实际调试时怎么快速看清 *args 和 **kwargs 到底收到了什么?
别靠猜。加一行 print(repr(args), repr(kwargs)) 最直接。尤其当调用链深、中间有装饰器时,参数可能被悄悄修改过。
常见现象:明明传了 name='alice',但 kwargs 里却没出现——大概率是上游某个装饰器或包装函数已经消费掉了这个 key。
-
args是 tuple,空的时候是(),不是None -
kwargs是 dict,空的时候是{},不是None - 如果函数被
@functools.wraps包装过,__signature__可能不反映真实接收逻辑,还是以运行时打印为准
参数签名和运行时行为经常不一致,尤其在用了装饰器或框架之后。看到 *args 和 **kwargs 不代表就能收全,得真跑一次、打一次印。


















