python中override父类方法的坑

最近在做数据预处理时,因为要使用父类的一些功能,所以希望继承父类实现自己的数据预处理子类。具体的实现是:子类重写了父类的方法,在子类的方法中显示调用了父类的同名该方法,结果运行后发现子类的方法被调用了多次,造成了结果与实际不一样。经过查看父类的该同名方法发现,父类方法实现过程中进行了递归调用,所以,在父类方法中有递归调用时,且子类方法重写时调用了父类方法,则需要注意多次调用的问题。

简化版的代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Base:
def test(self, n):
if n <= 1:
print(n)
else:
print(n)
self.test(n-1)

class MyBase(Base):
def test(self, n):
Base.test(self, n)
print("ok")

if __name__ == "__main__":
tb = MyBase()
tb.test(3)

父类的test方法进行了递归调用,子类重写父类的test方法,且在子类test方法中调用了父类的方法。运行结果如下:

1
2
3
4
5
6
3
2
1
ok
ok
ok

我们发现子类方法的print("ok")被调用了多次,经过单步调试后发现,父类的test方法在递归调用时,调用的是子类的test方法,所以会出现这个bug。仔细一下,应该是此处的父类的self引用已经指向了子类。因此,我们在父类test方法中显示调用父类的test方法,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Base:
def test(self, n):
if n <= 1:
print(n)
else:
print(n)
Base.test(self, n-1)

class MyBase(Base):
def test(self, n):
Base.test(self, n)
print("ok")

if __name__ == "__main__":
tb = MyBase()
tb.test(3)

运行后结果如我们预期:

1
2
3
4
3
2
1
ok

所以,关于继承关系中self引用的类已经改变了的猜想是对的。这个问题后续需要更加深入了解,self的具体机制,还有在重写基类方法时,如果需要显示调用基类方法,则注意基类方法中是否含有递归调用。