来自于 python 核心编程
重写
__init__
不会自动调用基类的__init__
类似于上面的覆盖非特殊方法,当从一个带构造器__init()__
的类派生,如果你不去覆盖
__init__()
,它将会被继承并自动调用。但如果你在子类中覆盖了__init__()
,子类被实例化时,基类的__init__()
就不会被自动调用。这可能会让了解 JAVA 的朋友感到吃惊。
class P(object):
def __init__(self):
print "calling P's constructor"
class C(P):
def __init__(self):
print "calling C's constructor"
>>> c = C()
calling C's constructor
如果你还想调用基类的
__init__()
,你需要像上边我们刚说的那样,明确指出,使用一个子类的实例去调用基类(未绑定)方法。相应地更新类 C,会出现下面预期的执行结果:
class C(P):
def __init__(self):
P.__init__(self)
print "calling C's constructor"
>>> c = C()
calling P's constructor
calling C's constructor
上边的例子中,子类的
__init__()
方法首先调用了基类的的__init__()
方法。这是相当普遍(不是强制)的做法,用来设置初始化基类,然后可以执行子类内部的设置。这个规则之所以有意义的原因是,你希望被继承的类的对象在子类构造器运行前能够很好地被初始化或作好准备工作,因为它(子类)可能需要或设置继承属性。
对 C++熟悉的朋友,可能会在派生类构造器声明时,通过在声明后面加上冒号和所要调用的所有基类构造器这种形式来调用基类构造器。而在 JAVA 中,不管程序员如何处理,子类构造器都会去调用基类的的构造器。
Python 使用基类名来调用类方法,对应在 JAVA 中,是用关键字 super 来实现的,这就是 super()
内建函数引入到 Python 中的原因,这样你就可以“依葫芦画瓢”了:
class C(P):
def __init__(self):
super(C, self).__init__()
print "calling C's constructor"
使用 super()的漂亮之处在于,你不需要明确给出任何基类名字...“跑腿事儿”,它帮你干了!
使用 super()的重点,是你不需要明确提供父类。这意味着如果你改变了类继承关系,你只需要改一行代码(class 语句本身)而不必在大量代码中去查找所有被修改的那个类的名字。 在 Python 3 中直接super().__init__()
即可,在 Python 2 中要老老实实按上面的写法来
顺带补充
经典类,使用深度优先算法。
因为新式类继承自 object,新的菱形类继承结构出现,问题也就接着而来了,所以必须新建一个 MRO。
新式类使用广度优先算法,更具体来说,是 C3 算法,核心在于 merge,C3 算法在某些情况下类似于广度优先算法。大家有兴趣可以了解一下。