120 lines
3.7 KiB
Python
120 lines
3.7 KiB
Python
# 要调用父类中的方法,可以使用super完成
|
||
class A:
|
||
def spam(self):
|
||
print('A.spam')
|
||
|
||
class B(A):
|
||
def spam(self):
|
||
print('B.spam')
|
||
super().spam()
|
||
|
||
# super的另一种常用方法是调用父类的__init__方法来确保父类被正确初始化了
|
||
class A:
|
||
def __init__(self):
|
||
self.x = 0
|
||
|
||
class B(A):
|
||
def __init__(self):
|
||
super().__init__()
|
||
self.y = 1
|
||
|
||
# 还有一种情况就是,你覆写了类中的方法,此时你可以调用super函数来使用原方法
|
||
class Proxy:
|
||
def __init__(self, obj):
|
||
self._obj = obj
|
||
|
||
def __getattr__(self, name):
|
||
return getattr(self._obj, name)
|
||
|
||
def __setattr__(self, name, value):
|
||
if name.startwith('_'):
|
||
super().__setattr__(name, value)
|
||
else:
|
||
setattr(self._obj, name, value)
|
||
|
||
# Python中的类继承
|
||
# 有些人有坏习惯,在子类中直接调用父类的方法,这在大部分时候能行
|
||
class Base:
|
||
def __init__(self):
|
||
print('Base.__init__')
|
||
|
||
class A(Base):
|
||
def __init__(self):
|
||
Base.__init__(self)
|
||
print('A.__init__')
|
||
|
||
# 但是如果这涉及到多重继承,那就会导致暴毙现象
|
||
class BaseV2:
|
||
def __init__(self):
|
||
print('Base.__init__')
|
||
|
||
class A1(BaseV2):
|
||
def __init__(self):
|
||
BaseV2.__init__(self)
|
||
print('A1.__init__')
|
||
|
||
class A2(BaseV2):
|
||
def __init__(self):
|
||
BaseV2.__init__(self)
|
||
print('A2.__init__')
|
||
|
||
class B1(A1, A2):
|
||
def __init__(self):
|
||
A1.__init__(self)
|
||
A2.__init__(self)
|
||
print('B1.__init__')
|
||
|
||
# 上面的继承关系就是标准的钻石继承,如果运行代码就会出现神奇魔法
|
||
b1 = B1()
|
||
# 哇!Base的__init__函数被调用了两次!这在一些场景下可能会导致麻烦,但如果使用super(),那就不会有这么多事了
|
||
|
||
class BaseV3:
|
||
def __init__(self):
|
||
print('Base.__init__')
|
||
|
||
class B1(BaseV3):
|
||
def __init__(self):
|
||
super().__init__()
|
||
print('B1.__init__')
|
||
|
||
class B2(BaseV3):
|
||
def __init__(self):
|
||
super().__init__()
|
||
print('B2.__init__')
|
||
|
||
class C1(B1, B2):
|
||
def __init__(self):
|
||
super().__init__()
|
||
print('C1.__init__')
|
||
# 这样的继承是健康的,每个init函数只被调用了一次
|
||
c1 = C1()
|
||
|
||
# 这是为啥嘞?每个类Python都会计算得出一个方法解析顺序列表MRO,在这个列表中会简单的对所有基类进行线性排列
|
||
print(C1.__mro__)
|
||
# MRO列表又是怎么确定的呢?Python使用了一种叫C3线性化处理的技术,这是一种归并排序,且满足三个约束
|
||
# 1.先检查子类再检查父类; 2.多个父类时按照列表顺序依次检查; 3.如果下一个待选的类出现了两个合法的选择,就从第一个父类中选取
|
||
# C3有点麻烦,但是计算的时候可以使用Python2.2更新的新式类MRO方法:
|
||
# 从左至右的深度优先遍历,但是如果遍历中出现重复的类,只保留最后一个,之后再根据去重前类的出现顺序排序
|
||
|
||
# 使用super函数时,会从MRO列表中的下一个类中搜索方法,只要每一个重定义过的方法都使用了super(),super就能保证让这个方法只被调用一次
|
||
# super(a, b)函数的本质是获取类a在类b的MRO列表中a的下一个类
|
||
# 但是这样也可能出现以下意想不到的孝bug
|
||
class D:
|
||
def spam(self):
|
||
print('D.spam')
|
||
super().spam()
|
||
|
||
class D1:
|
||
def spam(self):
|
||
print('D1.spam')
|
||
|
||
class D2(D, D1):
|
||
pass
|
||
|
||
d = D2()
|
||
d.spam()
|
||
|
||
# 这是由于MRO列表导致的super函数bug,这三个类的MRO列表应该是[D2, D, D1], super去找了D1中的方法
|
||
# 要避免遇到麻烦,最好遵守下面两点
|
||
# 1.确保所有的类中都实现了相同签名的调用; 2.确保你要调用的方法在顶层被实现(例子中为D2)
|