在python中如何重写类继承来的函数?

2020年6月6日 / 16次阅读 / Last Modified 2020年7月1日
面向对象

一开始还真不是很适应用类(OOP)的思路来组织代码,但慢慢也发现这种方法的好处了。编程高手追求的是更快更少更清晰,即速度更快,代码更少,结构更清晰。采用OOP的代码组织结构,是否能够更快不好说,但是更少和更清晰是很明显的。

类首先是一个namespace,将一堆变量和功能封装起来,更清晰了。通过继承,也实现了代码更少,相同的功能直接继承下来,代码结构呈立体的。在继承类中,如果定义了与父类型相同的函数方法,就算是重定义此函数了,包括__init__函数。在继承类中重定义父类型已有的函数,很多时候我们需要的是扩写,即在原父类型的函数功能基础上,再增加一些代码,这时就需要调用父类型的此函数。本文主要总结这个细节。

直接显示调用父类型的函数

这种方法简单粗暴,也有自己独特的应用场景。

class A:
    def out(self, string):
        print('In_A_'+string)

class B(A):
    def out(self, string):
        A.out(self, string)
        print('In_B_'+string)

B().out('pynote.net')

B继承A,重写out函数,在out中,首先直接显示调用父类A的out函数,执行效果如下:

D:\py>python super.py
In_A_pynote.net
In_B_pynote.net

这种调用方式,如果所有函数都这样重写,本质上就跟继承没有关系!

请看这段代码:

class A:
    def out(self, string):
        print('In_A_'+string)

class A2:
    def out2(self, string):
        print('In_A2_'+string)

class B(A):
    def out(self, string):
        A2.out2(self, string)
        print('In_B_'+string)


B().out('pynote.net')

B继承A,重写out函数,在out中,首先调用的却是一个不相干的类A2的out2函数。执行效果如下:

D:\py>python super.py
In_A2_pynote.net
In_B_pynote.net

这种直接显示调用类型.函数的方式,因为要传递self参数,因此调用的位置是否跟自己有继承关系,就不重要了!这叫直接显示的调用其它不相关类型的函数。但很多时候,因为代码组织,我们看到的都是用这种方式调用自己父类的函数。

这种方式的好处时清晰明了,就算是多继承也有的时候也可以。坏处就是因为是显示先出类名,所以后期代码维护如果类名变化,修改的地方会多一些。

多重继承的问题

上面这种直接显示调用其它类(可以不是父类),逻辑清晰,但是在多重继承的时候,会有点问题。

class A:
  def __init__(self):
    print("Enter A")
    print("Leave A")

class B(A):
  def __init__(self):
    print("Enter B")
    A.__init__(self)
    print("Leave B")

class C(A):
  def __init__(self):
    print("Enter C")
    A.__init__(self)
    print("Leave C")

class D(A):
  def __init__(self):
    print("Enter D")
    A.__init__(self)
    print("Leave D")

class E(B, C, D):
  def __init__(self):
    print("Enter E")
    B.__init__(self)
    C.__init__(self)
    D.__init__(self)
    print("Leave E")

E()

A所有类型的父类,BCD继承A,E继承BCD。他们都重写了__init__方法,而且都是用直接类名.函数的方式调用父类的__init__函数,执行效果如下:

D:\py>python super.py
Enter E
Enter B
Enter A
Leave A
Leave B
Enter C
Enter A
Leave A
Leave C
Enter D
Enter A
Leave A
Leave D
Leave E

问题很明显,A的__init__函数被执行了多次!

使用super函数

上面多重继承带来的问题,可以使用super函数来解决。

class A:
  def __init__(self):
    print("Enter A")
    print("Leave A")

class B(A):
  def __init__(self):
    print("Enter B")
    super().__init__()
    print("Leave B")

class C(A):
  def __init__(self):
    print("Enter C")
    super().__init__()
    print("Leave C")

class D(A):
  def __init__(self):
    print("Enter D")
    super().__init__()
    print("Leave D")

class E(B, C, D):
  def __init__(self):
    print("Enter E")
    super().__init__()
    print("Leave E")

E()

继承关系不变,BCDE的__init__函数都采用super()函数。执行效果如下:

D:\py>python super.py
Enter E
Enter B
Enter C
Enter D
Enter A
Leave A
Leave D
Leave C
Leave B
Leave E

E的初始化,不再重复调用A了!

在super机制里,可以保证公共父类仅被执行一次,至于执行的顺序,是按照MRO(Method Resolution Order)方法解析顺序 进行的。

值得注意的是,如果BCD的__init__不使用super函数,就没有上面这个效果。

不能用super的情况

对super的作用,理解还是很肤浅,有点说不清楚,下面这个示例,就不能用super。

class B():
  def __init__(self):
    print("Enter B")
    print("Leave B")

class C():
  def __init__(self):
    print("Enter C")
    print("Leave C")

class D():
  def __init__(self):
    print("Enter D")
    print("Leave D")

class E(B, C, D):
  def __init__(self):
    print("Enter E")
    super().__init__()
    print("Leave E")

E()

E继承自BCD,在E的__init__中直接使用super函数。执行效果如下:

D:\py>python super.py
Enter E
Enter B
Leave B
Leave E

显然E只初始化了B,而CD完全没有其作用。网上查到有人说,super主要用在多重继承的情况。还是一头雾水。有人说,使用super可以增加代码的可移植性,比如更换了继承的对象时。

任何在继承类中不重写的函数,包括__init__,都会通过MRO的方式去查找一个有此函数的父类并条用,包括__init__!特别注意初始化函数,一般类都有自己的__init__,多重继承的时候,如果不重写这个函数,一样也只是查找一个有此函数的父类__init__调用。比如下面的代码:

class B():
  def __init__(self):
    print("Enter B")
    print("Leave B")

class C():
  def __init__(self):
    print("Enter C")
    print("Leave C")

class D():
  def __init__(self):
    print("Enter D")
    print("Leave D")

class E(B, C, D): pass
  
E()

执行效果:

D:\py>python super.py
Enter B
Leave B

-- EOF --

本文链接:https://www.pynote.net/archives/2052

留言区

《在python中如何重写类继承来的函数?》有2条留言

电子邮件地址不会被公开。 必填项已用*标注

  • 麦新杰

    重写继承来的函数,在OOP里面的专业术语里,叫做多态,polymorphism [回复]

  • 麦新杰

    super返回的对象,跟MRO有关系。还要继续研究一下。 [回复]


前一篇:
后一篇:

More


©Copyright 麦新杰 Since 2019 Python笔记

go to top