共计 1224 个字符,预计需要花费 4 分钟才能阅读完成。
问题
在 Python 中定义类时,我们经常看到两种写法:
class PersonOne:
name = “person one”
class PersonTwo(object):
name = “person two”
也就是说在 Python 中,继承 object 与不继承 object 的写法有什么区别?在 Python2.X 中,第一种写法称之为经典类,第二种写法称之为新式类。Python2.2 之前只能支持第一种写法,在 Python2.2 到 Python2.7,两种写法都可以,但是不同写法定义出来的类是不一样的;在 Python3.X 中两种写法都可以,而且定义出来的类是完全一样的,都是新式类,可以理解为和 Java 一样,Python3.0 之后 Object 已经作为所有类的基类,因此是否显示指明已经不重要。因此,如果你是使用 Python3.X 的版本,完全可以无视这个问题,怎么写都行;但是如果不是就需要搞清楚区别了。
使用新式类和经典类的区别
个人理解,两种定义核心的区别就是定义类的 MRO 是不同的,在多重继承中,MRO 直接决定了方法的调用顺序,因此会产生很大的影响。
经典类的 MRO 经典类的 MRO 的生成时基于深度优先遍历算法的,以下面的继承关系为例,生成的 MRO 为:[D,B,A,C],因此,在调用 test 方法时,按照此顺序查找必然先找到 A 中的 test 方法,但是这其实是不太不合理的。
class A:
def test(self):
print(‘in a test’)
class B(A):
pass
class C(A):
def test(self):
print(‘in c test’)
class D(B, C):
pass
if __name__ == ‘__main__’:
d = D()
d.test()
新式类的 MRO 在新式类中,MRO 的生成时基于 C3 算法的,关于 C3 算法计算过程参见链接,核心就是 Merge 函数的计算过程,有的文章中直接说是广度优先其实并不准确。此处生成的类 D 的 MRO 为 [D,B,C,A],那么,按照这个顺序调用 test 方法自然找到的是 C 中的 test。
扩展
super 的调用关系
在 Python3 中,我们经常使用到 super 方法,在单继承中没有什么疑问,直接调用 super 方法自然是访问其唯一的父类。但是再多继承中,又是如何决定调用哪一个父类的呢?这个还是和 MRO 有关。准确来说,super 方法的调用方式为 super(ClassName, self).func(),那么此处的 func 方法就是属于 MRO 中 ClassName 下一个类。如果确实没有参数,即 super().func(),等价于 super(MRO 中的第一个类, self).func() 还是以上面的继承关系为例,重写类 D 的__init__() 方法,第一个 super 调用的是 B,第二个是 C。
class D(B, C):
def __init__(self):
print(“C”)
super().__init__()
super(B, self).__init__()