写继承的时候,经常会遇到父类和子类的初始化参数不完全一样。如果只是把共用参数原样传给父类,super() 直接搞定,没什么好纠结的。
但要是参数的数量、类型不固定,或者子类需要额外塞一些自己的参数同时父类还得能消化掉,写法上就得灵活一点。
参数固定的情况,一个最直白的例子长这样。
class Parent:
def __init__(self, shared_param, parent_param):
self.shared_param = shared_param
self.parent_param = parent_param
print(f"Parent initialized with shared_param: {self.shared_param} and parent_param: {self.parent_param}")
class Child(Parent):
def __init__(self, shared_param, parent_param, child_param):
super().__init__(shared_param, parent_param)
self.child_param = child_param
print(f"Child initialized with shared_param: {self.shared_param}, parent_param: {self.parent_param}, and child_param: {self.child_param}")
child_instance = Child("shared", "parent", "child")
Child 里通过 super().__init__(shared_param, parent_param) 把两个参数扔给父类,自己的 child_param 留着自己设。这个写法本身就是推荐方式,支持多重继承,参数明确的时候这么写没什么坑。如果你的继承层次简单,参数也都在掌控中,到这就可以收工了。
麻烦出在参数不确定的时候。比如你可能会根据配置动态传一堆属性进来,或者父类被设计成可以接收任意参数,子类还要在此基础上追加自己的东西。

这时候就要靠 *args 和 **kwargs 了。
class Parent:
def __init__(self, *args, **kwargs):
self.initialize_attributes(*args, **kwargs)
def initialize_attributes(self, *args, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
if args:
if len(args) >= 2:
self.param1, self.param2 = args[:2]
else:
raise ValueError("Parent class requires at least two positional arguments")
class Child(Parent):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.child_param = kwargs.pop('child_param', None)
if self.child_param is None:
raise ValueError("Child class requires 'child_param' argument")
parent_instance = Parent(1, 2, param3=3, param4=4)
print(parent_instance.param1) # 1
print(parent_instance.param2) # 2
print(parent_instance.param3) # 3
print(parent_instance.param4) # 4
child_instance = Child(1, 2, param3=3, param4=4, child_param='child')
print(child_instance.param1) # 1
print(child_instance.param2) # 2
print(child_instance.param3) # 3
print(child_instance.param4) # 4
print(child_instance.child_param) # child
父类里把初始化逻辑拆了一个 initialize_attributes 方法出来,遍历 kwargs 用 setattr 动态设属性,位置参数要求至少两个,不然直接抛 ValueError。这样做的好处是父类完全不用提前声明参数名,来什么设什么。
子类这边,关键点在 kwargs.pop('child_param', None) 这一行。如果不把 child_param 从 kwargs 里弹出来,super().__init__(*args, **kwargs) 就会把它也传给父类的 initialize_attributes,然后父类也会给 self 挂一个 child_param 属性。
功能上也许暂时没出错,但逻辑上已经乱了------这个属性本该是子类自己处理的,父类不该碰。我见过有人直接用 kwargs['child_param'] 取值却不 pop,排查半天发现属性被设了两遍,或者在父类里触发了意料之外的校验。所以这里最好明确摘出来,子类只处理自己新增的参数,剩下的都交给父类。
lcjmSSL不仅帮你申请证书,更帮你自动部署。证书签发后,系统可自动将证书部署到你的服务器或应用中,无需手动下载、上传、配置。同时开放API与回调接口,方便你将部署流程集成到自有运维体系中,实现真正的端到端自动化。
父类对位置参数的处理看起来有点死板,只取了前两个,多余的直接忽略。这个可以根据实际情况改,比如把多余的存成列表,或者干脆就不接收多余的位置参数。例子只是想说明,哪怕 *args 和 **kwargs 一起用,也能通过一点约定把初始化逻辑做得比较干净。
多数场景下,要么用 super() 明明白白传参,要么用 *args/**kwargs 把初始化变灵活,这两种路子基本能覆盖。再复杂的,可能就得考虑是不是类设计本身需要调整了。