Python方法解析顺序(MRO)详解:从__mro__到super多继承实战

何川
何川
发布时间:2026-10-14 | 6人浏览过
原创

本文深入解析Python的方法解析顺序(MRO),通过__mro__属性揭示类继承的查找优先级。对比单继承与多继承中super()与类名.__init__的行为差异,重点讲解钻石继承结构下的初始化调用链,确保每个父类方法仅执行一次,并提供多继承传参的最佳实践。

Python方法解析顺序(MRO)详解:从__mro__到super多继承实战

方法解析顺序(Method Resolution Order,简称MRO)

MRO基础:__mro__属性与查找优先级

让我们通过一段具体的代码示例来引入这个话题。

假设存在如下场景:


class A(object):
 def foo(self):
 print('A.foo()')

class B(object):
 def foo(self):
 print('B.foo()')

class C(B, A):
 pass

c = C()
c.foo()

类C同时继承了类A和类B,且这两个父类都定义了各自的foo()方法。此时,若创建C的实例c并调用c.foo(),Python解释器究竟会执行A.foo()还是B.foo()?

__mro__属性

在Python中,任何拥有父类的类都包含一个名为__mro__的特殊属性,它直接关联到方法解析顺序。该属性是一个元组(tuple),存储了方法查找时的对象遍历序列:序列中越靠前的类,其优先级越高。请运行以下代码进行验证:

print type(C.__mro__) print C.__mro__

控制台输出结果如下:

<type 'tuple'> (<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <type 'object'>)

从输出可见,B类位于C类之后、A类之前。因此在上述代码中,c.foo()实际调用的是B.foo()而非A.foo()。

造成B类排在C类前面的原因在于定义C类时父类的声明顺序:

class C(B, A):

如果将声明顺序调整为:

class C(A, B):

那么c.foo()将执行A.foo()。

熟悉操作系统环境变量的读者可以将__mro__类比为PATH环境变量:系统从左向右依次查找,一旦找到匹配的方法即执行并返回结果。

方法解析顺序

通过分析C.__mro__的值,我们可以明确Python的方法解析优先级从高到低依次为:

1. 实例本身(instance)

2. 当前类(class)

3. 父类(super class),继承关系越近或定义越早的类,优先级越高。

实际上,属性解析的顺序与上述过程基本一致,唯一的区别在于属性解析还会额外涉及__getattr__方法的查找(具体可参考Python对象的属性访问过程)。

继承机制:单继承与多继承概念解析

补充知识:python中的单继承,多继承和mro顺序

Python作为一种动态语言,与C++一样支持面向对象编程。面向对象编程具备三大核心特性:继承、封装和多态。本文将重点探讨Python语言中的单继承与多继承机制。

继承概念:

当一个类继承另一个类时,它将自动获得父类的所有属性和方法。原类称为父类(或基类),新类称为子类(或派生类、后代类)。子类不仅继承了父类的所有属性和方法,还可以定义自己特有的属性和方法。 单继承是指一个子类仅继承一个父类。

语法格式: class 子类(父类)

示例: class A(B)

在此例中,A类拥有B类的所有特征,即A类继承了B类。

B类称为父类或基类

A类称为子类、派生类或后代类

继承的主要作用在于功能的升级与扩展。 功能升级是对原有功能进行完善和重构,功能扩展则是添加原本不具备的功能。这种方式能有效减少代码冗余。

下面我们通过一个单继承的具体例子来演示:


class Dog(): #父类
 def __init__(self): #父类的属性初始化
 self.name='狗'
 self.leg=4
 def __str__(self):
 return "名字:%s %d 条腿"%(self.name,self.leg)

class Taidi(Dog): #定义一个Taidi 泰迪 类继承自Dog类 -->单继承
 pass

taidi=Taidi()
print(taidi) 输出结果--> 名字:狗 4 条腿

多继承:

多继承是指一个子类同时继承多个父类,这种结构有时也被称为菱形继承或钻石继承。

首先,我们需要了解多继承中一种常见的方法:单独调用父类方法。在子类初始化时,通常需要手动调用父类的初始化方法来构造父类属性,否则无法使用这些提供的属性。

在子类中调用父类初始化方法的格式为: 父类名.__init__(self)

下面是一个单独调用父类方法的示例:


print("******多继承使用类名.__init__ 发生的状态******")
class Parent(object): #父类
 def __init__(self, name):
 print('parent的init开始被调用')
 self.name = name #属性的初始化
 print('parent的init结束被调用')

class Son1(Parent): #单继承 Son1子类继承父类
 def __init__(self, name, age):
 print('Son1的init开始被调用')
 self.age = age
 Parent.__init__(self, name) #单独调用父类的属性
 print('Son1的init结束被调用')

class Son2(Parent): #也是单继承 Son2继承父类
 def __init__(self, name, gender):
 print('Son2的init开始被调用')
 self.gender = gender #单独调用父类的初始化属性方法
 Parent.__init__(self, name)
 print('Son2的init结束被调用')

class Grandson(Son1, Son2): #多继承,继承两个父类
 def __init__(self, name, age, gender):
 print('Grandson的init开始被调用')
 Son1.__init__(self, name, age) # 单独调用父类的初始化方法
 Son2.__init__(self, name, gender)
 print('Grandson的init结束被调用')

gs = Grandson('grandson', 18, '男') #实例化对象
print('姓名:', gs.name)
print('年龄:', gs.age)
print('性别:', gs.gender)

print("******多继承使用类名.__init__ 发生的状态******\n\n")

让我们查看上述代码的运行结果:


******多继承使用类名.__init__ 发生的状态******
Grandson的init开始被调用
Son1的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son1的init结束被调用
Son2的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son2的init结束被调用
Grandson的init结束被调用
姓名: grandson
年龄: 18
性别: 男
******多继承使用类名.__init__ 发生的状态******

多继承挑战:MRO顺序与super调用机制

MRO顺序

观察上述运行结果可以发现,由于采用了多继承结构,Parent类的属性被构造了两次。如果在更复杂的继承结构中,这种情况可能会更加严重。

为了解决这一问题,Python官方采用了一种算法,将复杂结构中的所有类映射到一个线性顺序上。依据这个顺序,可以保证每个类的方法仅被构造一次。这个顺序即为MRO顺序。

查看MRO的格式:

类名.__mro__

类名.mro()

多继承中super调用所有父类被重写的方法

super()的本质是利用MRO顺序去调用当前类在MRO序列中的下一个类。super().__init__()则调用下一个类的初始化方法来完成构造。


print("******多继承使用super().__init__ 发生的状态******")
class Parent(object):
 def __init__(self, name, *args, **kwargs): # 为避免多继承报错,使用不定长参数,接受参数
 print('parent的init开始被调用')
 self.name = name
 print('parent的init结束被调用')

class Son1(Parent):
 def __init__(self, name, age, *args, **kwargs): # 为避免多继承报错,使用不定长参数,接受参数
 print('Son1的init开始被调用')
 self.age = age
 super().__init__(name, *args, **kwargs) # 为避免多继承报错,使用不定长参数,接受参数
 print('Son1的init结束被调用')

class Son2(Parent):
 def __init__(self, name, gender, *args, **kwargs): # 为避免多继承报错,使用不定长参数,接受参数
 print('Son2的init开始被调用')
 self.gender = gender
 super().__init__(name, *args, **kwargs) # 为避免多继承报错,使用不定长参数,接受参数
 print('Son2的init结束被调用')

class Grandson(Son1, Son2):
 def __init__(self, name, age, gender):
 print('Grandson的init开始被调用')
 # 多继承时,相对于使用类名.__init__方法,要把每个父类全部写一遍
 # 而super只用一句话,执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因
 # super(Grandson, self).__init__(name, age, gender)
 super().__init__(name, age, gender)
 print('Grandson的init结束被调用')

print(Grandson.__mro__)

gs = Grandson('grandson', 18, '男')
print('姓名:', gs.name)
print('年龄:', gs.age)
print('性别:', gs.gender)
print("******多继承使用super().__init__ 发生的状态******\n\n")

查看上述代码的运行结果:


******多继承使用super().__init__ 发生的状态******
(<class '__main__.Grandson'>, <class '__main__.Son1'>, <class '__main__.Son2'>, <class '__main__.Parent'>, <class 'object'>)
Grandson的init开始被调用
Son1的init开始被调用
Son2的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son2的init结束被调用
Son1的init结束被调用
Grandson的init结束被调用
姓名: grandson
年龄: 18
性别: 男
******多继承使用super().__init__ 发生的状态******

单继承中super的使用


print("******单继承使用super().__init__ 发生的状态******")
class Parent(object):
 def __init__(self, name):
 print('parent的init开始被调用')
 self.name = name
 print('parent的init结束被调用')

class Son1(Parent):
 def __init__(self, name, age):
 print('Son1的init开始被调用')
 self.age = age
 super().__init__(name) # 单继承不能提供全部参数
 print('Son1的init结束被调用')

class Grandson(Son1):
 def __init__(self, name, age, gender):
 print('Grandson的init开始被调用')
 super().__init__(name, age) # 单继承不能提供全部参数
 print('Grandson的init结束被调用')

gs = Grandson('grandson', 12, '男')
print('姓名:', gs.name)
print('年龄:', gs.age)
#print('性别:', gs.gender)
print("******单继承使用super().__init__ 发生的状态******\n\n")

运行结果:


******单继承使用super().__init__ 发生的状态******
Grandson的init开始被调用
Son1的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son1的init结束被调用
Grandson的init结束被调用
姓名: grandson
年龄: 12
******单继承使用super().__init__ 发生的状态******

总结:super与类名调用的多继承差异

下面让我们对上述内容进行总结:

MRO机制保证了在多继承情况下,每个类只会出现一次。

在单继承场景中,super().__init__与类名.__init__的用法基本没有差异。

但在多继承场景下存在显著区别:super方法能确保每个父类的方法仅执行一次,而直接使用类名调用方法会导致方法被执行多次。

在多继承中,由于Python中super算法的特性,必须将所有参数全部传递,否则会导致报错。

在单继承中,使用super方法时不能传递全部参数,只能传递父类方法所需的参数,否则也会报错。

多继承时,若使用类名.__init__方法,需要逐一列出每个父类;而使用super方法只需一行代码即可执行所有父类的方法。这也是为何多继承需要传递全部参数的原因。

下面是一个简单的面试题示例:


class Parent(object):
 x = 1

class Child1(Parent):
 pass

class Child2(Parent):
 pass

print(Parent.x, Child1.x, Child2.x)
Child1.x = 2
print(Parent.x, Child1.x, Child2.x)
Parent.x = 3
print(Parent.x, Child1.x, Child2.x)

运行结果:


1 1 1
1 2 1
3 2 3

以上关于Python方法解析顺序(MRO)的浅显探讨就是全部内容,希望能为大家提供参考,也希望大家多多支持本站。

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表