`

super在多继承中的调用细节

阅读更多
注:此处以python 3为运行环境,例子摘自《python cookbook》第8章。

python中若子类要实现父类的初始化,主要有两种方法,第一种是直接通过父类名,第二种是利用super方法。在单继承时两者没什么区别,但在多继承时就需要注意一些细微的差距了。实例解释才是硬道理!
1、利用父类名的情况:
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__')

此时实例化C类会输出如下:
>>> c = C()
Base.__init__
A.__init__
Base.__init__
B.__init__
C.__init__
>>>

从中可看出Base类被调用了两次。这想必在很多情况下都不是我们想要的结果,所以此时可考虑用super方法。

2、利用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__')

此时再实例化C类的输出为:
>>> c = C()
Base.__init__
B.__init__
A.__init__
C.__init__
>>>

可看出Base类是不是只调用了一次啊!但很遗憾的是,这并不是促使我写这篇博客记录的原因,因为如果仔细观察的话,虽说Base类的确如预期只调用了一次,但你有没有发觉是先输出“B.__init__”而后才输出的“A.__init__”?而且为什么这样就使得Base只初始化了一次?想必你也有点懵逼了吧?其实这一切都得“怪罪”于super在多继承时的调用过程。python在实现一个类(不仅是继承)时,会产生一个方法生成解析顺序列表,该列表可通过类属性 __mro__ 查看之,如本例中是这样的:
>>> C.__mro__
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>,
<class '__main__.Base'>, <class 'object'>)
>>>

所以在搜索一个属性或方法时,它就会按照这个列表遍历每个类,直到找到第一个匹配这个属性或方法的类为止。而在继承中使用super时,解释器会每遇到一次super就会在该列表上搜索下一个类,直到不再遇到super或列表遍历完为止,然后再类似递归逐层返回。因此本例中搜索过程为:C中遇到super --> 搜索列表中的下一个类,即A --> A中再次遇到super,搜索B --> B中super再现,搜索Base --> 初始化Base类,递归返回。
为了更好的解释该过程,现在请注释掉B类的super所在行:
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__')

再次实例化C类,输出如下:
>>> c = C()
B.__init__
A.__init__
C.__init__

Base类不再产生输出!为什么?因为B中没了super后,就阻断了列表去搜索Base类,所以也就没有初始化Base了!
分享到:
评论

相关推荐

    276.274.JAVA基础教程_面向对象(中)-super调用属性和方法(276).rar

    在这个"JAVA基础教程_面向对象(中)-super调用属性和方法"的主题中,我们将深入探讨Java中的面向对象特性,特别是如何使用`super`关键字来访问父类的属性和方法。 面向对象编程(Object-Oriented Programming,OOP)...

    JAVA中的继承学习总结

    这些访问控制决定了成员可以在多大范围内被访问。 #### 七、总结 继承是Java语言的一个重要特性,它可以帮助我们更好地组织代码、提高代码的复用性。通过理解继承的概念、关键字的用法、成员的访问控制以及多态性...

    09【继承、super、this、抽象类】1

    【继承、super、this、抽象类】是Java编程语言中面向对象编程的重要概念。本章节主要探讨了四个关键点:继承、方法重写、super关键字和抽象类。 1. **继承**: - **概述**:继承允许一个类(子类)从另一个类...

    SuperSocket.ClientEngine.Core socket 客户端处理粘包半包

    - **处理数据**:在`ReceiveNextAsync`方法中,根据协议规范读取数据,判断是否完成一个完整的消息,如果完成则调用`CompleteReceive`方法通知SuperSocket处理下一个数据包。 4. **示例代码**: ```csharp ...

    类继承的初始化顺序类,继承的初始化顺序

    如果没有显式地在子类构造函数中调用`super()`,编译器会自动添加一个默认的`super()`调用,这意味着会调用基类的无参构造函数。 #### 成员变量的初始化 成员变量的初始化顺序非常重要。在一个类中,成员变量按照...

    SuperSocket.dll文件

    在SuperSocket框架中,`SuperSocket.SocketBase.dll`是基础类库,提供了基本的Socket服务端接口和抽象类,如ServerBase和AppSession,它们是所有服务端和会话类的基础。通过继承这些类,开发者可以自定义自己的服务...

    java---- 封装,接口,继承,覆盖,构造过程,多态,static、this、super、final用法

    封装是指将对象的状态(属性)和行为(方法)捆绑在一起,并且隐藏对象的内部实现细节,只暴露必要的接口供外部调用的过程。 **特点:** 1. **隐藏内部实现细节:** 对象的内部状态和实现细节被隐藏起来,用户只能...

    类的继承.zip

    - 在Python和Java等语言中,`super()`关键字用于调用父类的构造函数或方法,确保初始化过程的正确进行。 6. 多继承: - 一个子类可以继承多个父类,这被称为多重继承。它增加了灵活性,但也可能导致命名冲突和...

    java练习题-继承.doc

    例如,`Human`类可能有一个构造方法`Human(String name, int age)`,而`Student`类可以有`Student(String name, int age, String gender)`,并且在子类的构造方法中使用`super(name, age)`来初始化从父类继承的属性...

    Python中的单继承与多继承实例分析

    在多继承中,如果存在同名方法,Python按照基类在定义中的顺序从左到右进行搜索,先在`sample`中搜索方法,如果没有找到,然后依次搜索`speaker`和`student`。如果需要指定使用哪个基类的方法,可以明确地调用它,如...

    H5 调用android 相机拍照

    在移动应用开发中,混合开发模式常常被采用,其中HTML5...当然,实际应用中还需要考虑更多细节,比如图片的压缩、用户权限的处理、拍照结果的反馈等。这只是一个基础的实现,开发者可以根据具体需求进行扩展和优化。

    学号父类程序调用

    在编程领域,"学号父类程序调用"这一主题主要涉及到面向对象编程中的继承概念。在Java、Python、C#等面向对象编程语言中,父类(也称为基类或超类)是用于创建子类的基础。父类定义了一组通用属性和方法,子类可以...

    javascript控件开发之继承关系

    在实际的控件开发中,我们还需要考虑更多的细节,比如事件委托、数据绑定、异步操作、性能优化等。为了更好地组织和管理代码,可以使用模块化工具,如CommonJS、AMD或者现代的ES6模块。此外,库和框架如React、Vue、...

    继承与多态讲义

    - **基类(Base Class / Super Class)**:被继承的类,通常包含一些通用的属性和方法。 - **派生类(Derived Class / Sub Class)**:继承自基类的类,可以添加自己的特性和方法。 - **派生树**:通过继承形成的...

    《java面向对象程序设计-继承和多态》教案.doc

    通过实现接口,类可以拥有接口中声明的所有方法,这在单一继承机制下提供了更多的灵活性。接口还常用于定义常量,提供常量值的统一管理。 总结来说,Java的面向对象设计包括封装、继承和多态,这些特性使得代码更加...

    浅谈Python中的继承

    - **理解**:实例化过程中,对象并不会直接拥有类的所有属性和方法,而是在需要时通过类查找并调用它们。如果对象修改了某个属性,则会在对象的命名空间中创建一个新的属性。 - **示例**: ```python class ...

    JAVA继承、抽象类、接口[定义].pdf

    - 在子类的构造方法中,可以使用`super()`来调用父类的特定构造方法。`super()`调用必须位于子类构造方法的第一行。 - 如果父类有多个构造方法,`super()`可以根据传递的参数选择对应的父类构造方法进行调用。 - ...

    java 远程方法调用

    Java 远程方法调用(Remote Method Invocation,RMI)是一种在分布式环境中执行对象方法的技术。它允许一个Java应用程序调用运行在不同JVM(Java虚拟机)上的另一个对象的方法,就像是在本地调用一样。在Java RMI中...

    面向对象之继承-JAVA面试资料

    总的来说,Java中的继承提供了代码复用、类的层次结构构建以及多态性的基础,是面向对象编程中不可或缺的一部分。通过合理使用继承,开发者可以创建更加灵活、可扩展的代码结构,以应对复杂的问题和需求。

    JAVA 子类继承父类的范例 可直接运行

    在继承关系中,子类不应该过于依赖父类的具体实现细节,以避免在父类发生变化时影响到子类。遵循面向对象设计原则中的开闭原则和里氏替换原则,有助于编写健壮且易于维护的代码。 最后,需要注意的是代码中提到的`...

Global site tag (gtag.js) - Google Analytics