一开始还真不是很适应用类(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 --