单继承

简介: 单继承

子类为了调用父类(超类)的一个方法,可以使用 super() 函数,比如:

class A:
    def spam(self):
        print('A.spam')
class B(A):
    def spam(self):
        print('B.spam')
        super().spam()  # Call parent spam()

super() 函数的一个常见用法是在 __init__() 方法中确保父类被正确的初始化了:

class A:
    def __init__(self):
        self.x = 0
class B(A):
    def __init__(self):
        super().__init__()
        self.y = 1

也可以这样直接调用父类的一个方法:

class Base:
    def __init__(self):
        print('Base.__init__')
class A(Base):
    def __init__(self):
        Base.__init__(self)
        print('A.__init__')

1.2. 多继承

涉及到多继承的代码中就有可能导致很奇怪的问题发生。 比如,考虑如下的情况:

class Base:
    def __init__(self):
        print('Base.__init__')
class A(Base):
    def __init__(self):
        Base.__init__(self)
        print('A.__init__')
class B(Base):
    def __init__(self):
        Base.__init__(self)
        print('B.__init__')
class C(A,B):
    def __init__(self):
        A.__init__(self)
        B.__init__(self)
        print('C.__init__')

如果你运行这段代码就会发现 Base.__init__() 被调用两次,如下所示:

>>> c = C()
Base.__init__
A.__init__
Base.__init__
B.__init__
C.__init__
>>>

假设你在代码中换成使用 super() ,结果就很完美了:

class Base:
    def __init__(self):
        print('Base.__init__')
class A(Base):
    def __init__(self):
        super().__init__()
        print('A.__init__')
class B(Base):
    def __init__(self):
        super().__init__()
        print('B.__init__')
class C(A,B):
    def __init__(self):
        super().__init__()  # Only one call to super() here
        print('C.__init__')

运行这个新版本后,你会发现每个 __init__() 方法只会被调用一次了:

>>> c = C()
Base.__init__
B.__init__
A.__init__
C.__init__
>>>

为了弄清它的原理,我们需要花点时间解释下Python是如何实现继承的。 对于你定义的每一个类,Python会计算出一个所谓的方法解析顺序(MRO)列表。 这个MRO列表就是一个简单的所有基类的线性顺序表。例如:

>>> C.__mro__
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>,
<class '__main__.Base'>, <class 'object'>)
>>>

为了实现继承,Python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。

而这个MRO列表的构造是通过一个C3线性化算法来实现的。 我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:

  • 子类会先于父类被检查
  • 多个父类会根据它们在列表中的顺序被检查
  • 如果对下一个类存在两个合法的选择,选择第一个父类

你所要知道的就是MRO列表中的类顺序会让你定义的任意类层级关系变得有意义。

当你使用 super() 函数时,Python会在MRO列表上继续搜索下一个类。 只要每个重定义的方法统一使用 super() 并只调用它一次, 那么控制流最终会遍历完整个MRO列表,每个方法也只会被调用一次。 这也是为什么在第二个例子中你不会调用两次 Base.__init__() 的原因。

super() 有个令人吃惊的地方是它并不一定去查找某个类在MRO中下一个直接父类, 你甚至可以在一个没有直接父类的类中使用它。例如,考虑如下这个类:

class A:
    def spam(self):
        print('A.spam')
        super().spam()

如果你试着直接使用这个类就会出错:

>>> a = A()
>>> a.spam()
A.spam
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "<stdin>", line 4, in spam
AttributeError: 'super' object has no attribute 'spam'
>>>

但是,如果你使用多继承的话看看会发生什么:

>>> class B:
...     def spam(self):
...         print('B.spam')
...
>>> class C(A,B):
...     pass
...
>>> c = C()
>>> c.spam()
A.spam
B.spam
>>>
相关文章
|
编译器 C++
48 C++ - 多继承
48 C++ - 多继承
60 0
|
C++
52 C++ - 纯虚函数和多继承
52 C++ - 纯虚函数和多继承
25 0
|
6月前
|
C++
C++程序中的多重继承
C++程序中的多重继承
66 1
|
6月前
|
C++
50多重继承
50多重继承
28 0
|
6月前
|
C++
C++多继承与多态
C++多继承与多态
20 0
|
6月前
|
存储 NoSQL 编译器
『 C++类与对象 』多态之单继承与多继承的虚函数表
『 C++类与对象 』多态之单继承与多继承的虚函数表
|
6月前
|
存储 NoSQL C++
『 C++类与对象 』多继承与虚继承
『 C++类与对象 』多继承与虚继承
|
6月前
|
C++
C++继承、多继承及菱形继承
C++继承、多继承及菱形继承
|
Java 编译器 PHP
C++的多重继承
派生类都只有一个基类,称为单继承(Single Inheritance)。除此之外,C++也支持多继承(Multiple Inheritance),即一个派生类可以有两个或多个基类。 多继承容易让代码逻辑复杂、思路混乱,一直备受争议,中小型项目中较少使用,后来的 Java、C#、PHP 等干脆取消了多继承。 多继承的语法也很简单,将多个基类用逗号隔开即可。例如已声明了类A、类B和类C,那么可以这样来声明派生类D: class D: public A, private B, protected C{ //类D新增加的成员 } D 是多继承形式的派生类,它以公有的方式继承 A 类,
C++单继承和多继承
C++单继承和多继承