调用super()时父类方法为何调用子类重写方法?求正确处理方式
面向对象问题:子类方法签名冲突与动态分派解析
问题描述
尝试创建父类的子类,期望在实例化时为父类的部分方法设置默认值,但运行时报错,子类方法签名覆盖父类后,父类方法调用了子类重写的方法导致参数不匹配。
复现代码:
class Base: def __init__(self, factor): self.factor = factor def add(self, a, b): return a + b def add_and_multiply(self, a, b): return self.add(a, b) * self.factor class DefaultClass(Base): def __init__(self, factor, a): self.a = a def add(self, b): return super().add(self.a, b) def add_and_multiply(self, b): return super().add_and_multiply(self.a, b) b = Base(5) d = DefaultClass(5, 1) print(b.add_and_multiply(1, 1)) print(d.add_and_multiply(1))
报错信息:
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In [27], line 24 22 d = DefaultClass(5, 1) 23 print(b.add_and_multiply(1, 1)) ---> 24 print(d.add_and_multiply(1)) Cell In [27], line 19, in DefaultClass.add_and_multiply(self, b) 18 def add_and_multiply(self, b): ---> 19 return super().add_and_multiply(self.a, b) Cell In [27], line 9, in Base.add_and_multiply(self, a, b) 8 def add_and_multiply(self, a, b): ----> 9 return self.add(a, b) * self.factor TypeError: add() takes 2 positional arguments but 3 were given
核心概念:动态方法分派(Dynamic Method Dispatch)
Python采用动态方法分派机制:当通过self调用方法时,解释器会根据self实际所属的类(即实例的类型)来决定执行哪个方法,而不是根据编写这段调用代码的类。
在你的代码中,当调用d.add_and_multiply(1)时:
- 子类
DefaultClass的add_and_multiply调用父类Base的同名方法 - 父类
Base的add_and_multiply中执行self.add(a, b),这里的self是DefaultClass的实例d - 因此会调用子类
DefaultClass重写后的add方法,但该方法只接受self和b两个参数,父类却传入了3个参数(self,a,b),最终触发TypeError。
另外还要注意:子类DefaultClass的__init__没有调用父类的__init__,会导致self.factor属性缺失,即使参数问题解决,后续也会报错。
正确处理方式
方式1:保持方法签名兼容(推荐)
子类重写方法时,保留与父类一致的方法签名,通过默认参数实现"固定部分参数"的需求,同时记得调用父类构造方法:
class Base: def __init__(self, factor): self.factor = factor def add(self, a, b): return a + b def add_and_multiply(self, a, b): return self.add(a, b) * self.factor class DefaultClass(Base): def __init__(self, factor, a): super().__init__(factor) # 必须调用父类构造方法初始化factor self.a = a # 保留父类add的签名,通过默认参数使用实例的self.a def add(self, b, a=None): target_a = a if a is not None else self.a return super().add(target_a, b) def add_and_multiply(self, b): return super().add_and_multiply(self.a, b) b = Base(5) d = DefaultClass(5, 1) print(b.add_and_multiply(1, 1)) # 输出:(1+1)*5=10 print(d.add_and_multiply(1)) # 输出:(1+1)*5=10 # 也可以临时传入自定义a值 print(d.add(2, 3)) # 输出:3+2=5
方式2:明确指定调用父类方法(不推荐)
如果强制让父类方法调用自身的add而非子类的,可以在父类中直接指定类名调用方法,但这会破坏多态性,不建议使用:
class Base: def __init__(self, factor): self.factor = factor def add(self, a, b): return a + b def add_and_multiply(self, a, b): # 明确调用Base类的add方法,绕过动态分派 return Base.add(self, a, b) * self.factor # 子类代码不变,此时父类add_and_multiply会调用自己的add,而非子类的
方式3:使用组合替代继承
如果子类的方法逻辑与父类差异较大,组合是更合适的选择,避免继承带来的方法签名冲突:
class Base: def __init__(self, factor): self.factor = factor def add(self, a, b): return a + b def add_and_multiply(self, a, b): return self.add(a, b) * self.factor class DefaultClass: def __init__(self, factor, a): self.base_instance = Base(factor) # 组合Base实例 self.a = a def add(self, b): return self.base_instance.add(self.a, b) def add_and_multiply(self, b): return self.base_instance.add_and_multiply(self.a, b) b = Base(5) d = DefaultClass(5, 1) print(b.add_and_multiply(1, 1)) # 输出10 print(d.add_and_multiply(1)) # 输出10
内容的提问来源于stack exchange,提问作者JimmyJackson
相关产品推荐
相关产品推荐

