必须在类方法中显式声明args和*kwargs,否则父类调用会因参数透传失败而抛TypeError;装饰器、Mixin及动态参数场景(如JSON反序列化、argparse)均依赖其保障参数兼容性与复用性。

类方法里不写 *args 和 **kwargs 会卡在父类调用上
子类重写 __init__ 或其他方法时,如果父类签名带可变参数,而你没留出接收通道,super().xxx() 就可能抛 TypeError: unexpected keyword argument。这不是你漏传参,是父类内部逻辑依赖了未声明的参数入口。
常见于继承 tkinter.Widget、dataclasses.field 相关类、或第三方库的基类(比如 SQLModel 的 __init__)。它们往往把额外参数透传给底层构造器,但不会显式列出所有支持的参数名。
- 必须把
*args放在普通参数之后、**kwargs之前 -
**kwargs必须放在参数列表最末尾 - 哪怕当前子类逻辑完全不处理这些参数,也得收着——否则父类调用链就断了
装饰器和 mixin 类强制要求参数透传
写一个通用装饰器包装类方法,或设计一个 Mixin 提供日志/缓存/权限校验能力时,你无法预判被增强的方法接受什么参数。硬编码参数列表会导致装饰器只能适配特定函数,彻底失去复用价值。
例如这个简易日志 mixin:
立即学习“Python免费学习笔记(深入)”;
class LoggingMixin:
def log_call(self, func, *args, **kwargs):
print(f"Calling {func.__name__} with {len(args)} pos + {len(kwargs)} kw args")
return func(self, *args, **kwargs)
如果不用 *args 和 **kwargs,你就得为每个被包装的方法单独写一遍逻辑,根本没法抽象。
动态参数绑定场景下,缺它直接报错
从配置加载参数、解析命令行、反序列化 JSON 创建实例时,参数名和数量都是运行时决定的。比如:
- 用
json.loads()读到一个字典{"name": "Alice", "score": 95, "tags": ["py", "dev"]},想直接传给User.__init__ - 调用
argparse.ArgumentParser解析后得到一堆关键字,要喂给某个类构造器
此时你只能靠 **kwargs 接收;若构造器还允许位置参数(如 id 固定放第一个),就得靠 *args 补位。漏掉任意一个,**config_dict 就会触发 TypeError: __init__() got an unexpected keyword argument。
容易忽略的细节:解包和收集不是对称操作
定义时用 *args 是“收集”,调用时用 *args 是“解包”——这是同一个符号的两种相反行为,初学者常混淆。
比如你有一个列表 params = [1, "hello"]:
-
func(*params)→ 把列表解开成两个位置参数传进去 -
def func(*args): ...→ 把所有位置参数收进args元组
同理,** 在定义和调用中也各干各的事。真正难的是在类继承链里,既要收、又要转、还要可能再解包——这时候少一个 * 或多一个 **,错误信息往往不指向问题根源,而是爆在第三层调用里。


















