super()函数的使用

本文深入探讨Python中的super()函数,解释其与MRO列表的关系,以及如何在多重继承中正确使用super(),避免代码重复,实现优雅的继承扩展。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

@(python进阶)[super, 继承, mro]


018.11.10

说明

一些Python入门书并没有讲清楚super()函数应该怎么用,只在说继承知识点的时候顺便一笔带过。为此我糊涂了好久、好久……

继承

Python中的继承很容易实现,如下:

class A(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def greet(self):
        print("I'm A")

    def show(self):
        
        string = ("My name is {name}.\n"
                  "I'm {age}.".format(name=self.name, age=self.age))
        print(string)


class B(A):
    pass


if __name__ == "__main__":
    b = B("Guan", 18)
    b.greet()
    b.show()

# 输出:
I'm A
My name is Guan.
I'm 18.

所以继承父类的时候,super()函数并非必要

那什么时候确切的要用到super()呢?
答:欲求不满时。

使用super()

之所以需要“继承”,大部分原因是我们想要扩展基础类——需要派生类有更多的功能。当然了,如果只是额外增加一个“独立”功能,仍然用不到super()。如下:

class A(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def greet(self):
        print("I'm A")

    def show(self):
        
        string = ("My name is {name}.\n"
                  "I'm {age}.".format(name=self.name, age=self.age))
        print(string)


class B(A):
    # “独立”功能
    def cry(self):
        print("cry ...")


if __name__ == "__main__":
    b = B("Guan", 18)
    b.cry()

# 输出:
cry ... 

B类中的cry()是一个与A类完全无关的函数,所以直接定义就OK。但此时需求变了,我们需要B类的对象可以在自我介绍的时候先说hello。我们当然可以用如下方式重载show():

class B(A):
    def show(self):
        print("ya~hello~")
        string = ("My name is {name}.\n"
                  "I'm {age}.".format(name=self.name, age=self.age))
        print(string)

可这样做重复了A中的部分代码,很不“程序员”。所以super()出场了:

class B(A):
    def show(self):
        print("ya~hello~")
        super().show()

但python2中需要这样写:

class B(A):
    def show(self):
        print("ya~hello~")
        super(B, self).show()  # 需要传入自己的类名以及对象self

乍一看super()的意思是:获取父类。而super().show()就是调用父类的show()函数。是,也不是

真实的super()

super()获取的是MRO列表中下一个类。什么是MRO呢?这里不是重点,它有自己计算的方式,有兴趣可以自己找找资料。

示例:

class A(object):
    pass

class B(A):
    pass

if __name__ == "__main__":
	print(B.mro())

输出内容如下:
[<class '__main__.B'>, <class '__main__.A'>, <class 'object'>]

在MRO列表中,B的下一个类是A。所以在B类中的super(),代表A。这里刚好A是B的父类。也有不刚好的时候。

class A(object):
    def __init__(self):
        print("A")
        super().__init__()


class B(A):
    def __init__(self):
        print("B")
        super().__init__()


class C(A):
    def __init__(self):
        print("C")
        super().__init__()


class D(B, C):
    def __init__(self):
        print("D")
        super().__init__()


if __name__ == "__main__":
    d = D()
    print(D.mro())

# 输出:
D
B
C
A
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

注意ABCD的输出顺序。可以看到,B中调用super().__init__(),没有打印“A”,而是“C”,这是符合MRO列表顺序的。

总结

  • super()与父类没有直接关系
  • super()获取的是MRO列表中下一个类

感谢

参考个人博客 FunHacks
https://funhacks.net/explore-python/Class/super.html
参考慕课老师 bobby
https://www.bilibili.com/video/av28281876/?p=22

### Super 函数的用法与原理 #### 1. **Super 函数的基本功能** `super()` 是 Python 提供的一个内置函数,主要作用是用来调用父类的方法。它的核心目的是解决多重继承时方法查找顺序的问题,并提供了一种优雅的方式来访问父类中的方法[^2]。 在单继承情况下,虽然可以直接通过 `ParentClass.method(self, ...)` 的方式来显式调用父类方法,但这并不是最佳实践。因为如果将来需要更改继承关系(比如将父类替换为另一个类),则需要手动更新所有硬编码的地方。而使用 `super()` 可以使代码更具灵活性和可维护性[^3]。 --- #### 2. **Super 函数的工作机制** `super()` 的工作基于 MRO (Method Resolution Order),即方法解析顺序。当在一个类中调用 `super()` 时,实际上会返回一个代理对象,这个代理对象指向当前类之后的第一个父类及其后续的 MRO 列表[^1]。 以下是具体的流程: - 当前类以及其实例作为参数传递给 `super()`。 - 返回的对象会在 MRO 列表中继续向下寻找合适的目标方法。 - 找到目标方法后执行之。 例如下面的例子展示了如何利用 `super()` 调用父类初始化方法: ```python class Parent: def __init__(self, name): self.name = name print(f"Parent init called with {name}") class Child(Parent): def __init__(self, name, age): super().__init__(name) # 调用父类的构造函数 self.age = age print(f"Child init called with {age}") child_instance = Child('Alice', 10) ``` 在这个例子中,当我们创建 `Child` 类的新实例时,不仅设置了自身的属性 `age`,还借助 `super()` 自动完成了对父类 `Parent` 初始化逻辑的调用。 --- #### 3. **Super 在多重继承下的表现** 考虑如下复杂情况: ```python class Base1: def greet(self): print("Hello from Base1") class Base2: def greet(self): print("Hi there! From Base2") class Derived(Base1, Base2): def greet(self): super().greet() derived_obj = Derived() derived_obj.greet() # 输出什么? ``` 这里的关键在于理解 `Derived` 的 MRO 序列是什么样的。对于上述代码片段来说,`Derived.__mro__` 结果将是 `(Derived, Base1, Base2, object)`。所以当我们在 `Derived.greet()` 方法内部调用了 `super().greet()` 后,实际上是沿着这条链路找到第一个实现了 `greet` 方法的类 —— 即 `Base1`。 因此最终输出应为 `"Hello from Base1"`。 需要注意的是,即使交换了基类声明位置变成 `class Derived(Base2, Base1):...` ,那么新的 MRO 将变为 `(Derived, Base2, Base1, object)`,从而导致不同的行为结果:"Hi there! From Base2"[^3]。 --- #### 4. **无参 vs 带参形式的区别** 除了最常见的零参数写法外,`super(type[, instance])` 支持带两个参数的形式。这允许更灵活地指定上下文中使用的类型及关联实例/子类。不过通常推荐采用简洁版语法除非确实遇到特殊需求才需回退至完整签名模式下操作[^1]。 简单对比两者差异如下所示: | 特性 | 零参数形式 | 完整两参数形式 | |-------------------|----------------------------|---------------------------| | 场景适用范围 | 默认适用于当前所在类 | 明确指定了参与运算的具体类别 | | 是否依赖隐式的 self 参数 | Yes | No | 典型应用场景可能包括单元测试框架设计等领域当中需要用到动态切换不同层次间相互协作的情形之下更为常见一些[^2]。 --- ### 总结 综上所述,`super()` 不仅简化了传统意义上繁琐的手工管理父类引用过程,更重要的是它能够很好地配合 Python 多重继承体系结构一起发挥作用,使得程序具备更强健的生命力与适应能力面对未来可能出现的变化挑战! ---
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值