Python内置super实为类而非函数 动态MRO机制可跳过直接父类

【python】B站最细致的super()详解,一定有你不知道的知识! — 码农高天

码农高天

super并非查找直接父类的语法糖,其本质是接收类与实例的双参数对象,执行逻辑由传入对象的动态MRO链决定,多继承下会直接调用平级兄弟类。

AI-assisted summary of 【python】B站最细致的super()详解,一定有你不知道的知识! — original source. Check the original for full context.

Source published:

Key ideas

  • super 在 Python 中并非函数、方法或关键字,而是一个正儿八经的内置类(在 CPython 中对应 PySuper_Type),每次调用 super(...) 实际上是实例化了一个 super 对象。 (0:00)
  • super 的完整原型接收两个参数:第一个参数(类)决定从 MRO 链的哪一个位置向后检索,第二个参数(对象或类)决定以谁的 MRO 为基准并将方法绑定到该实体上。 (0:00)
  • 在显式传递两个参数的前提下,super 完全可以在类定义外部任意使用,例如在未初始化实例的外部直接调用父类的 __init__ 方法。 (5:00)
  • 在复杂多继承场景中,super() 不一定会调用该类声明的直属父类,而是严格按照运行时传入实例的 MRO 链顺序查找,甚至可能直接调用其平级类的同名方法。 (10:00)

Detailed notes

## Background 该内容由知名技术博主“码农高天”于 2023 年 10 月 30 日发布在 YouTube 平台。视频简介指出,本期主题应观众要求深入讲解 Python 中的 `super()` 机制,并结合先前关于 MRO(方法解析顺序)的内容展开进阶剖析。

## TL;DR 视频首先纠正了关于 `super` 是函数或关键字的常见误区,指出其本质是 CPython 底层的内置类 `PySuper_Type`。随后,作者详细拆解了 `super(type, object_or_type)` 的双参数运行原理,演示了如何通过第一个参数跳过特定类检索方法,并展示了在类定义外部直接调用 `super` 的黑魔法。最后,视频通过经典的多继承案例揭示了协和继承的动态调用陷阱,证明在特定 MRO 链下父类的 `super()` 可能会调用到看似无关的平级类方法。

## Atmosphere & style 视频呈现出硬核干货与轻松幽默兼备的技术极客风格,主讲人讲解严密、循序渐进,充满探究源码底层的求真热情。随着由浅入深的活体代码演练和反直觉报错的抛出,讲解节奏张弛有度,在“颠覆日常认知”与“原来如此”的顿悟感之间自如切换,极具思维启发性与观赏趣味。

## Bulletin - [00:00](https://www.youtube.com/watch?v=bfuBVu7YALk&t=0s) super 在 Python 中并非函数、方法或关键字,而是一个正儿八经的内置类(在 CPython 中对应 PySuper_Type),每次调用 super(...) 实际上是实例化了一个 super 对象。 - [00:00](https://www.youtube.com/watch?v=bfuBVu7YALk&t=0s) super 的完整原型接收两个参数:第一个参数(类)决定从 MRO 链的哪一个位置向后检索,第二个参数(对象或类)决定以谁的 MRO 为基准并将方法绑定到该实体上。 - [05:00](https://www.youtube.com/watch?v=bfuBVu7YALk&t=300s) 在显式传递两个参数的前提下,super 完全可以在类定义外部任意使用,例如在未初始化实例的外部直接调用父类的 __init__ 方法。 - [10:00](https://www.youtube.com/watch?v=bfuBVu7YALk&t=600s) 在复杂多继承场景中,super() 不一定会调用该类声明的直属父类,而是严格按照运行时传入实例的 MRO 链顺序查找,甚至可能直接调用其平级类的同名方法。

## Key takeaways - [00:00](https://www.youtube.com/watch?v=bfuBVu7YALk&t=0s) super 并不是内置函数或关键字,其实质是 Python 底层的内置类型(CPython 源码中为 PySuper_Type),调用 super(...) 本质上是构造一个 super 类的实例对象。 - [00:00](https://www.youtube.com/watch?v=bfuBVu7YALk&t=0s) 通常书写的无参形式 super() 只是语法糖,其完整形态为 super(type, object_or_type),接收两个必不可少的逻辑参数。 - [00:00](https://www.youtube.com/watch?v=bfuBVu7YALk&t=0s) super 的第一个参数(type)指定查找的起始参照点,其底层逻辑是跳过该类本身,从 MRO 链条中排在该类之后的下一个类开始检索。 - [00:00](https://www.youtube.com/watch?v=bfuBVu7YALk&t=0s) super 的第二个参数(object 或 type)提供用于检索的基准 MRO 链,同时在检索到目标函数后将其与该对象完成方法绑定(bind)。 - [00:00](https://www.youtube.com/watch?v=bfuBVu7YALk&t=0s) 相比硬编码父类名称(如 Person.__init__(self)),使用 super() 不仅便于后续重构与基类改名解耦,其核心价值在于支持动态调度(Dynamic Dispatch)。 - [05:00](https://www.youtube.com/watch?v=bfuBVu7YALk&t=300s) 通过手动调整 super 的第一个参数,可以精确控制向上检索的起点,甚至能够刻意跳过中间层的父类初始化逻辑,直接调用更上层的祖先类。 - [05:00](https://www.youtube.com/watch?v=bfuBVu7YALk&t=300s) super 并非必须在类体内使用;在模块全局或任意函数中,配合 Male.__new__ 生成的裸对象,直接执行 super(Male, m).__init__(...) 同样能合法执行并完成属性初始化。 - [05:00](https://www.youtube.com/watch?v=bfuBVu7YALk&t=300s) 无参数 super() 只能安全运行在类定义内部,Python 解释器在底层通过字节码等机制自动推断当前所处的类作用域和当前函数传入的第一个参数(self 或 cls)。 - [05:00](https://www.youtube.com/watch?v=bfuBVu7YALk&t=300s) 单参数形态 super(type) 会返回一个未绑定的 super 对象(unbound object),主要用于特殊元编程或配合描述符协议手动绑定。 - [10:00](https://www.youtube.com/watch?v=bfuBVu7YALk&t=600s) 在多继承场景下,由于方法解析严格依照传入实例(如子类 M)的完整 MRO 链,父类 B 中的 super() 可能会调用其平级类 C 中的方法,颠覆纯单继承的直觉。 - [10:00](https://www.youtube.com/watch?v=bfuBVu7YALk&t=600s) 这一机制在面向对象编程中被称为协同多继承(Cooperative Multiple Inheritance),要求开发者必须从实例整体的 MRO 拓扑顺序理解 super 的跳转逻辑,而非单纯视为找父类。

## Detailed walkthrough ### [00:00](https://www.youtube.com/watch?v=bfuBVu7YALk&t=0s) super() 的本质与完整形态

很多 Python 开发者对 `super` 的认知通常停留在最基础的用法:定义一个子类(例如 `Male`)继承自父类(例如 `Person`),然后在子类的 `__init__` 方法中通过 `super().__init__(...)` 来调用父类的初始化函数。对于许多人来说,这几乎就是关于 `super` 的全部知识。然而,关于 `super` 存在一个广泛流传的误区——不少人认为它是一个函数、方法,甚至有人误以为它是关键字。

码农高天纠正了这个冷知识:“实际上 super 是一个正儿八经的 class,它是一个内置类的名字,然后 super 括号并不是调用了一个函数,super 括号是建立了一个 super 的对象。”在 CPython 源码的 `typeobject.c` 文件中,它的定义被称为 `PySuper_Type`。它与大家平常频繁接触的 `list`、`dict` 完全一样,都是底层的 `type`,在 Python 语境下就是一个类。

```mermaid flowchart TD accTitle: super完整双参数工作机制 accDescr: 展示super接收类与对象参数后,如何依托MRO链定位方法并绑定执行

subgraph Parameters ["super(type, object_or_type)"] P1["第一个参数: type (Class)<br/>决定在 MRO 链中从哪个位置往后找"] P2["第二个参数: object / type<br/>决定提供 MRO 链,并绑定函数"] end

subgraph Process ["MRO 链检索与绑定流程"] direction TB MRO["获取 self 的 MRO 链:<br/>[Male, Person, Animal, object]"] Find["定位第一个参数 Male 的下一个节点:<br/>Person"] Lookup["在 Person 中查找目标函数 __init__"] Bind["将找到的方法 bind 到 self 上执行"] end

P2 -->|"提供 MRO 链"| MRO P1 -->|"跳过该类"| Find MRO --> Find Find --> Lookup Lookup --> Bind ```

通常大家习惯书写不带参数的 `super()`,但 `super` 的完整形态实际上接收两个参数:`super(type, object_or_type)`。 - **第一个参数(type)**:必须是一个类,它决定了在方法解析顺序(MRO)链条上从哪一个类**之后**开始往后查找。 - **第二个参数(type 或 object)**:可以是一个对象实例或类,它决定了两件事:第一,最终解析到的函数将绑定(bind)到哪一个对象或类上;第二,它决定了**以谁的 MRO 链**作为查找基准。

在 `Male` 类的初始化中,`super().__init__(name)` 的完整等价形式其实是 `super(Male, self).__init__(name)`。其背后的完整运作流程如下: 1. 从传入的第二个参数 `self` 中取出其 MRO 链(例如 `[Male, Person, object]`)。 2. 在该链条中定位第一个参数 `Male` 所在的位置。 3. 从 `Male` 紧随其后的那个类开始检索(跳过 `Male` 本身,第一个找到的是 `Person`)。 4. 检查 `Person` 中是否存在目标函数(如 `__init__`)。 5. 找到该函数后,将其绑定到 `self` 上并执行,其效果等同于调用 `Person.__init__(self, name)`。

为什么不直接硬编码写成 `Person.__init__(self, name)` 呢? - **代码重构与解耦**:如果未来基类名称发生变更(例如将 `Person` 改名为 `HomoSapiens`),或者修改了继承关系,直接使用硬编码名字会导致所有子类都需要逐处修改,极易引入 Bug;而使用 `super` 则会自动顺着 MRO 去寻找正确的类。 - **动态性机制(Dynamic Dispatch)**:很多教程不会讲到的核心原因在于,`super` 是完全动态的。“它会根据 self 的 MRO 进行寻找,而 self 也就是传进来的 argument 它本身是动态的。”在不改动当前函数源码的情况下,由于传入对象的不同,`super` 拿到的实际调用类也会动态发生改变。

---

[05:00](https://www.youtube.com/watch?v=bfuBVu7YALk&t=300s) 参数控制与类外调用黑魔法

为了进一步拆解两个参数的独立作用,高天引入了三层继承关系进行验证:`Male` 继承自 `Person`,而 `Person` 又继承自 `Animal`。在 `Animal` 中需要初始化参数 `age`。如果一切正常运行,`super(Male, self).__init__(age, name)` 会依次调用 `Person` 与 `Animal` 的初始化函数。

然而,若在第 15 行故意改写成 `super(Person, self).__init__(age, name)`,程序便会立刻报出 `TypeError`,提示 `__init__` 只需要两个位置参数。出现此现象的原因正是参数控制机制: - 当前 `self` 的完整 MRO 链是 `[Male, Person, Animal, object]`。 - 因为传入的第一个参数是 `Person`,`super` 会从 `Person` 之后开始查找,直接跳到了 `Animal`。 - `Animal.__init__` 只接收 `self` 和 `age` 两个参数,而代码多传了 `name`,导致报错。 - 如果将调用改为只传入 `age`(即 `super(Person, self).__init__(age)`),程序便会成功初始化出一个仅有 `age` 与 `gender`、跳过了 `Person` 初始化步骤的 `Male` 对象。

```mermaid flowchart LR accTitle: super(Person, self) 跳过 Person 类示例 accDescr: 展示在 MRO 链 Male -> Person -> Animal -> object 中,第一个参数指定为 Person 时会直接跳过 Person 检索 Animal

A["Male (实例 MRO 起点)"] --> B["Person (被第一个参数指定)"] B -.->|"跳过该类,从后一位查找"| C["Animal (命中 __init__)"] C --> D["object"] ```

这也证明了 `super` 绝非只能写在类定义(class definition)体内。只要掌握了它的双参数形态,在类的外部同样可以任意调用: - 在外部实例化一个未经初始化的对象 `m = Male.__new__(Male)` 后,可以直接在模块全局级别执行: ```python super(Male, m).__init__(18, "Peter") ``` - 此时 `super` 会顺着 `m` 的 MRO 链,越过 `Male` 查找到 `Person.__init__`,并成功绑定到对象 `m` 上完成所有父类属性的初始化。高天向观众抛出互动调查:“在看这期视频之前,你知道 super 可以在 class 之外使用吗?”

关于参数形态的进阶机制: - **单参数形式 `super(type)`**:返回的是一个未绑定对象(unbound object),必须在后续手动将其绑定到其他对象上。这种用法极为少见,涉及高级元编程与描述符(descriptor)机制。 - **无参数形式 `super()`**:只能安全地用于类定义内部。Python 在底层使用字节码注入等“黑魔法”自动补齐了这两个参数: 1. 寻找当前 `super()` 是在哪个 class 的作用域内被定义的,将其自动填入第一个参数。 2. 寻找当前方法执行时传入的第一个实参(通常是 `self` 或 `cls`),自动将其填入第二个参数。

---

[10:00](https://www.youtube.com/watch?v=bfuBVu7YALk&t=600s) 多继承下的动态调用陷阱

在多继承场景下,`super` 的动态性展示得最为淋漓尽致,也最容易颠覆程序员的直觉。

高天设计了一个经典的多继承示例。最初的代码结构非常线性: - 类 `A` 定义了方法 `c()`,输出 `"A"`。 - 类 `B` 继承自 `A`,在其 `c()` 方法中调用 `super().c()`。 - 类 `M` 继承自 `B`,在其 `c()` 方法中同样调用 `super().c()`。 - 当执行 `m = M(); m.c()` 时,调用链条平直无奇:`M.c -> B.c -> A.c`,控制台打印出 `"A"`。

```mermaid flowchart TD accTitle: 多继承 MRO 导致 super 调用平级类的机制 accDescr: 展示 M 继承 B 和 C,B 和 C 继承 A 时,M 的 MRO 链使得 B 中的 super().c() 调用了平级类 C 中的方法而非父类 A

subgraph Inheritance ["继承层次结构"] A_class["Class A (def c: print 'A')"] B_class["Class B (继承 A, 调用 super().c())"] C_class["Class C (继承 A, def c: print 'C')"] M_class["Class M (继承 B, C)"]

A_class --> B_class A_class --> C_class B_class --> M_class C_class --> M_class end

subgraph Execution ["m.c() 实际沿 MRO 执行流"] direction LR step1["M"] -->|"super()"| step2["B"] step2 -->|"super() 在 MRO 中向后找"| step3["C (打印 'C')"] step3 -->|"super()"| step4["A"] end ```

随后引入变化:定义一个新类 `C` 同样继承自 `A`,且 `C` 的 `c()` 方法打印 `"C"`。接着,让 `M` 同时继承 `B` 与 `C`(即 `class M(B, C): pass`)。此时整个类 `B` 的代码未做任何改动,`B` 的父类声明依旧清清楚楚写着是 `A`。

当调用 `m.c()` 时,终端打印出来的结果不是 `"A"`,而是 `"C"`!即使在 `M` 的定义中故意写死调用 `B.c(self)`,打印结果也依然是 `"C"`。

在单继承思维下,这个行为非常反直觉:类 `B` 的父类明明是 `A`,为什么在 `B` 里面写 `super().c()`,最终执行的却是八竿子打不着的平级类 `C` 的代码?

高天强调,核心原因正是之前阐述的原理: - 在 `B` 内部执行的 `super().c()`,等价于 `super(B, self).c()`。 - 关键在于此时传进去的 `self` 是子类 `M` 的实例,查找必须依据 **`self` 的 MRO 链条**。 - `M` 的 MRO 顺序是 `[M, B, C, A, object]`。 - `super(B, self)` 的第一参数是 `B`,因此它必须在 `M` 的 MRO 链中跳过 `B`,寻找排在 `B` **后面的下一个类**——这恰恰就是 `C`,而不是 `A`。

这就是面向对象编程中著名的协和多继承协同机制(Cooperative Multiple Inheritance)。“所以很多人会发现,自己对 super 的理解其实并不那么透彻,它并不是单纯地去找父类这么简单。”高天总结道,如果不深刻理解 Python 的 MRO 机制与 `super` 的双参数本质,在复杂继承体系中就会写出完全违背预期的代码。

## Notable quotes > [00:00](https://www.youtube.com/watch?v=bfuBVu7YALk&t=0s) 实际上 super 是一个正儿八经的 class,它是一个内置类的名字,然后 super 括号并不是调用了一个函数,super 括号是建立了一个 super 的对象。 — 码农高天

## Mentioned 码农高天, Python, CPython, MRO

## Video description 这期视频聊一下super,也是之前有人想让我讲的内容,正好可以用到前面的MRO的知识~

[Source](https://www.youtube.com/watch?v=bfuBVu7YALk)

中文简报

Python内置super实为类而非函数 动态MRO机制可跳过直接父类 super并非查找直接父类的语法糖,其本质是接收类与实例的双参数对象,执行逻辑由传入对象的动态MRO链决定,多继承下会直接调用平级兄弟类。 视频首先纠正了关于 `super` 是函数或关键字的常见误区,指出其本质是 CPython 底层的内置类 `PySuper_Type`。随后,作者详细拆解了 `super(type, object_or_type)` 的双参数运行原理,演示了如何通过第一个参数跳过特定类检索方法,并展示了在类定义外部直接调用 `super` 的黑魔法。最后,视频通过经典的多继承案例揭示了协和继承的动态调用陷阱,证明在特定 MRO 链下父类的 `super()` 可能会调用到看似无关的平级类方法。 super 在 Python 中并非函数、方法或关键字,而是一个正儿八经的内置类(在 CPython 中对应 PySuper_Type),每次调用 super(...) 实际上是实例化了一个 super 对象。 (0:00) super 的完整原型接收两个参数:第一个参数(类)决定从 MRO 链的哪一个位置向后检索,第二个参数(对象或类)决定以谁的 MRO 为基准并将方法绑定到该实体上。 (0:00) 在显式传递两个参数的前提下,super 完全可以在类定义外部任意使用,例如在未初始化实例的外部直接调用父类的 __init__ 方法。 (5:00) 在复杂多继承场景中,super() 不一定会调用该类声明的直属父类,而是严格按照运行时传入实例的 MRO 链顺序查找,甚至可能直接调用其平级类的同名方法。 (10:00)

Reading summary

码农高天指出,Python 中的 `super` 并非函数或关键字,而是一个内置类;其核心价值在于依据传入实例的动态方法解析顺序(MRO)链查找并绑定方法,而非静态指向父类。 super 的底层类型与双参数本质 (0:00) 许多开发者习惯将 `super` 视作调用父类方法的特殊语法糖或关键字,但其在 CPython 源码(`typeobject.c`)中实际被定义为名为 `PySuper_Type` 的内置类,与 `list`、`dict` 同属底层 `type`。常见的无参调用 `super()` 是编译器自动注入上下文的简写,其完整原型为 `super(type, object_or_type)`。其中第一个参数必须是类,决定在 MRO 链条中从哪个类之后开始向后查找;第二个参数是对象实例或类,既提供检索所需的 MRO 链,也决定解析到的方法最终绑定到谁身上执行。 参数控制、跳过继承节点与类外调用 (5:00) 显式传递双参数能够精细控制方法解析的起始点。在 `Male` 继承 `Person`、`Person` 继承 `Animal` 的三层体系中,如果在 `Male` 内部将首参数指定为 `super(Person, self)`,解析过程便会越过 `Person` 直接检索 `Animal` 的初始化方法。此时若传递多余的 `name` 参数即会触发 `TypeError`,而若仅传入 `age`,则可成功构造出绕过 `Person` 初始化的实例。这种机制打破了“`super` 只能在类定义内部调用”的固有印象。只要通过 `Male.__new__(Male)` 构造未初始化对象 `m`,在模块全局直接执行 `super(Male, m).__init__(18, "Peter")`,同样能顺着 `m` 的 MRO 链完成属性初始化。此外,单参数的 `super(type)` 返回的是未绑定对象,通常涉及元编程与描述符机制;而无参调用则只能在类内部依赖字节码机制自动补全当前类与首个形参(如 `self` 或 `cls`)。 多继承协同调用与动态分发陷阱 (10:00) 单继承思维下容易误以为 `super()` 只会沿着父类向上追溯,但在多继承场景下,这种认知会导致反直觉的执行结果。设想类 `B` 与 `C` 均继承自 `A`,且各自重写了方法 `c()`;类 `M` 同时继承 `B` 与 `C`。在 `B` 的源码完全未修改且父类明示为 `A` 的前提下,通过 `M` 的实例调用 `super().c()` 时,控制台打印出的却是平级类 `C` 的结果。高天分析指出,这是由于 `B` 内的 `super().c()` 实质是 `super(B, self).c()`。此时接收到的 `self` 是子类 `M` 的实例,所依托的 MRO 链为 `[M, B, C, A, object]`。当以 `B` 为基准向后检索时,排在下一个位置的正是平级类 `C`。这种协作式多继承(Cooperative Multiple Inheritance)机制使代码行为完全取决于运行时传入对象的实际 MRO,而非静态声明。 深入理解 `super` 的双参数原理及其依赖实例 MRO 的动态分发机制,是避免在 Python 复杂多继承体系中写出逻辑错误代码的关键前提。

Source quotes

[0:00] 码农高天: 实际上 super 是一个正儿八经的 class,它是一个内置类的名字,然后 super 括号并不是调用了一个函数,super 括号是建立了一个 super 的对象。

Original transcript

[0:00] 这期视频我们来介绍一下Super我相信大部分的人可能使用super呢都使用的是这种方式就是我有一个class比如说是may 继承另外一个class比如说是person然后我在这个mail也就是它子类的init凯数里面用super括号.init来调用它负类的触手化函数可能这个就是很多人掌握的Super的全部知识了所以这次我们照惯例争取让大家更加详细和深入的了解一下super这个东西我们首先介绍一个冷知识啊很多人会告诉你 super是一个方法或者是一个函数我还见过有人说super是关键字的实际上super是一个正儿八经的class它是一个内置类的名字然后super括号并不是调用了一个函数 super括号是建立了一个super的对象如果大家感兴趣的话,super的defination在typeobject.c里面它叫Pi SuperType他和我们平时很常接触到的list dictionary一样 它是一个type那你从python的角度理解了 你就可以认为它是一个class好 那我们回过头来接着说super尽管我们更常用的形式是super括号里面什么都没有但是Super的完整版它里面应该是有两个参数的第一个参数是一个type 也就是一个class而第二个参数是一个type或者是一个object其中第二个参数决定了这一个函数 bind 到哪一个 object 或者 class 上同时第二个参数决定了使用哪个MRO关于MRO大家翻我之前的视频而第一个参数决定了在MRO链上从哪个class开始往后找这么说比较抽象啊 我们把它实力化一下我们看左边刚才的这个super括号.init它是等价于super括号没有这是一个class对不对逗号self 这是一个object对不对第二 init name的我们把这个程序运行一下可以看到它的结果就是我们想象中的结果这个object里面有个gender是male,有个name是Peter那这个super male.hard self做了什么事情呢首先他要从 self 这个 object 里面拿到 mro如果你看过之前那期视频的话 你会知道self的mr非常简单 就是没有person object对不对然后他会找到第一个argument 也就是没有在mro里所处的位置那在当前这个情况下mail就是最开始的那个对不对male person object对吧接下来他会从没有后面的那个class开始找就是没有的不要 从没有后面开始那他第一个找到的就是person然后他就看person里面有没有init这个函数然后发现有enet这个函数然后他再把这个init函数给bind到self上这个bind的过程未来我们有机会细讲那在这里你可以理解为这个person的引力坛数传进去的这个self就是super里面的self也就是说左边的这行代码等价于这段代码它相当于靠了一个person.initself name好 那你可能问了 那为什么我们不直接使用person.init self name呢那么有几个原因第一你未来有可能会改变鸡肋的名字甚至会改变继承的方式比如未来我们把class person改名了叫class homo sapiens那在这种情况下如果我用super的话我就什么都不用管因为它会自动追随着mro找到正确的class但是如果我用这种命名的class的话我就要全部都修改这样更容易引入错误那当然这一点你可能能自己领悟到或者听很多教程说过但是还有一点是教程几乎不会说一般人也想不到的就是super这件事它其实是动态的它会根据self的mro进行寻找而self也就是传进来的argument它本身是动态的也就是说同样一个函数里面我用super在不改变这个函数的情况下我有可能会拿到不同的class这个问题等等我们细讲我们先说回这个super本身我们现在又给他增加了一层继承的层级我们说Person也是一个animal然后所有的Animal都有一个年龄这个时候如果我们在第15行这里用super male self他就会正常的initialize所有的东西它会靠这个person的init然后person的init会靠这个animal的init最好就完成了这个mail那如果我们把第15行改成super person self会有什么结果呢大家可以看他报错了他说Init函数只要两个positional arguments [5:00] 这是为什么呢因为当我们使用 super person self的时候Self的MRO链是male,person,animal,object那第一个argument由于是person所以他会从person后面的class 开始找animal是有引内谈数的 但是animal的引内谈数只take一个age所以当我们传入age name的时候他就错了那为了实验一下啊 我们可以只传进去这个age可以看到呢 我们就初始画了一个只有age和gender的male object因为我们跳过了person的一内谈数举这个例子 还是想跟大家说明 super 的这两个argument也就是第一个这个type和第二个这个type或者object分别决定了什么第二个是决定使用这个函数的对象和MRO而第一个只决定了在MRO这个链上从哪开始找那当然在我们举的例子里面这第二个argument是一个object对吧它也可以是一个class这种形式就是在class method的时候会比较有用这个未来我们有机会再展开讲吧好那super的完全体形态大家一定要记住因为你只有理解了它的完全体形态你才会在任何时刻都知道它到底在干什么比如说你现在就可以知道如果我加上第19行这段代码我就可以把这个m object给完整的做初始化了你会发现Super这个东西并不是只能在class里面使用的它可以在任何一个地方使用我只要给定第二个参数object或者class再给定第一个参数从哪开始找我就能使用它的函数啊这里我们就是说从m这个object的mro上寻找没有后面开始的引力谈数这样我们实际上就找到了person的init函数然后再用person的init函数对m这个object做initialization大家可以看这个结果就跟我们之前正确的那个initialization的结果一样了好了 作为一个小调查 在看这期视频之前 你知道super可以在class之外使用吗好 那在了解了super的完全体之后呢 我们继续往下讲Super的两个参数都是可选参数当你只给super一个参数的时候呢,它返回的是unbound object你需要把它Bind到一个别的object上才能正确的使用那这个用法呢相对罕见而且一般是要比较进阶的编程的时候才会用得到在这里我们就先不深入的聊了也是未来我们讲bound method的时候讲descript的时候然后我们再去聊它我们平时接触到的最常见的用法实际上就是这个super括号里面什么都给这种 super 的用法是必须要在class definition 里面进行的为什么呢 因为这个super用一些python的黑魔法做了两件事第一他会寻找自己是在哪个class里面被定义的然后把这个class放到它的第一个参数里第二,他会寻找自己是在哪个函数里被定义的然后把这个函数的第一个argument放到他第二个参数里那在当前这种情况下呢 super 括号就等价于 super male self因为它是在没有这个class里被定义的Init这个函数第一个argument是self今天由于时间关系 我们就不去分析它背后的黑魔法了大家只要记住这个关系就可以了它的完全体是什么当它没有参数的时候是从哪找到的这两个参数好 那我们之前留了一个扣啊 我们说super这个东西呢 由于pythondynamic的特性同样的函数定义在针对不同的object时候会产生不同的效果我们在这里举一个例子我们看左边这边代码非常的简单 class a 有个函数是c然后就是打印A然后B继承了AB的C函数就是用super.C然后M继承了BM的C函数也是super.C我们建立了一个m的object 然后用m.c 这里就非常直观对不对m找到了b的c 然后b再找到了a的c 最后打印了一个a我们可以看到结果和我们的预期是一致的没有任何惊喜啊好 现在我们引入一个新的class C我们看在MBA完全没有改变的情况下我们引入了一个新的class C这个C继承A这个C也有一个C函数c函数printc接下来我们让我们的class m在计成b的同时还计成c根据我们在之前视频学到的知识 现在m但MRO应该是B [10:00] See, eight of them. It's just when M is super.c.s, it's class B. 请问打印结果是什么 You can't think it's C. We can even change the 15th class as b.cself, to use the b-c-seal. You can see that the result is still in the C This time you will find a very strange thing B class with C one more time it is a However, the class is super, but there is a number of C. This is a rather rather a non-final thing. The reason we've mentioned that the 7th of the value is super be the self and the way to find the method is from the MRO. This self is an M object. So in search of B, the second is C and not A This is also the class B in super, but not class of a number of different variables. That's why there are many people who found that they have a super understanding of the idea of a super. It's not so simple That's the video I hope you enjoyed this video. If you like it, please don't forget to subscribe and hit the like button. Don't forget to leave the like button. Thank you.