一个python的super和mro问题今天让我纠结了一早上,源自于看tornado的源代码对于super这个“方法”产生的困惑,对于老鸟mro应该是常识了,对于小白而言,尼玛搞懂这个掉了我好几根头发。
谷歌了一下找到一篇博客也在讨论这个问题,博主列举了一个例子我觉得很典型,同时他也提出了和我一样的问题,我在原文的引用中高亮了。下面摘自“JohnsonGuo的专栏”:
有一天某同事设计了一个相对复杂的类体系结构(我们先不要管这个类体系设计得是否合理,仅把这个例子作为一个题目来研究就好),代码如代码段4:
代码段4:
class A(object):
def __init__(self):
print "enter A"
print "leave A"
class B(object):
def __init__(self):
print "enter B"
print "leave B"
class C(A):
def __init__(self):
print "enter C"
super(C, self).__init__()
print "leave C"
class D(A):
def __init__(self):
print "enter D"
super(D, self).__init__()
print "leave D"
class E(B, C):
def __init__(self):
print "enter E"
B.__init__(self)
C.__init__(self)
print "leave E"
class F(E, D):
def __init__(self):
print "enter F"
E.__init__(self)
D.__init__(self)
print "leave F"
>>> f = F()
enter F
enter E
enter B
leave B
enter C
enter D
enter A
leave A
leave D
leave C
leave E
enter D
enter A
leave A
leave D
leave F
明显地,类A和类D的初始化函数被重复调用了2次,这并不是我们所期望的结果!我们所期望的结果是最多只有类A的初始化函数被调用2次——其实这是多继承的类体系必须面对的问题。我们把代码段4的类体系画出来,如下图:
按我们对super的理解,从图中可以看出,在调用类C的初始化函数时,应该是调用类A的初始化函数,但事实上却调用了类D的初始化函数。好一个诡异的问题!
作者翻阅python源代码后得出的结论对我非常有帮助,同样摘抄如下,感谢JohnsonGuo的分享:
1. super并不是一个函数,是一个类名,形如super(B, self)事实上调用了super类的初始化函数,
产生了一个super对象;
2. super类的初始化函数并没有做什么特殊的操作,只是简单记录了类类型和具体实例;
3. super(B, self).func的调用并不是用于调用当前类的父类的func函数;
4. Python的多继承类是通过mro的方式来保证各个父类的函数被逐一调用,而且保证每个父类函数
只调用一次(如果每个类都使用super);
5. 混用super类和非绑定的函数是一个危险行为,这可能导致应该调用的父类函数没有调用或者一
个父类函数被调用多次。
顺着作者的结论我想补充一条:
如作者所说super并不是像我们想象中一样直接找到当前类的父类,而是沿着mro顺藤摸瓜,所以有可能出现作者原文中提到的问题!
补充阅读:
对于想要了解更多细节而不愿意翻阅Python源码的人(比如我。。),强烈推荐阅读Python的这篇文档:“The Python 2.3 Method Resolution Order”,看下来试下来,基本就了解的八九不离十了。
相关推荐
标题中的“Python库 | python-mro-language-server-0.0.1.tar.gz”指的是一个针对Python编程语言的特定库,名为“python-mro-language-server”,版本号为0.0.1,它被压缩成一个tar.gz文件进行分发。这个库可能是一...
在Python中,可以通过`__mro__`属性查看一个类的MRO,也可以使用`mro()`方法动态计算MRO。例如: ```python class A: pass class B(A): pass class C(A): pass class D(B, C): pass print(D.__mro__) # ...
在Python中,`mro_tools`是一个特定的库,版本为0.2.0,它支持Python 2和3两种解释器,表明它是跨版本兼容的。这个库的发布格式是一个名为`.whl`的文件,这是一种预编译的Python包格式,旨在简化安装过程,避免了...
在Python 2.3版本引入了`__mro__`属性,它是一个元组,按照MRO顺序存储了类的层次结构。当我们通过`super()`函数调用方法时,Python会按照MRO顺序查找并执行方法。 以下是一些关于Python多继承和MRO顺序的关键点: ...
Python 107.mro().mp4
标题中的“圣诞树代码编程python-24-拓展-mro顺序”揭示了这是一个关于Python编程的教程,特别涉及到了Python的多继承(Multiple Inheritance)和方法解析顺序(Method Resolution Order,简称MRO)。在Python中,...
Python的方法解析顺序(Method Resolution Order,MRO)是面向对象编程中一个关键的概念,尤其是在处理多继承时。MRO决定了当一个实例调用一个方法时,Python如何确定应该使用哪个父类的方法。MRO遵循一定的规则,...
我们都已经知道,Python 3(Python 2 的新式类)中多继承模式是使用 C3 算法来确定 MRO(Method Resolution Order) 的。 下面就讲解C3算法具体是怎么计算的。 MRO计算规则 首先来定义一些符号: 用CN表示一个类:C1, C2...
python库。 资源全名:mro_tools-0.1.2-py2.py3-none-any.whl
3.6版本中的类属性(class attributes)和方法解析顺序(MRO)是重要的概念。 5. **模块与包**:Python的模块化设计使得代码可重用性增强,而包则提供了更高级的组织结构。3.6中,可以使用`importlib`模块动态加载...
Python作为一种强大的编程语言,提供了多种库来处理XML文档,如`ElementTree`,`lxml`等。本主题主要探讨如何使用Python解析XML文件,并将数据存入数据库。 首先,`批量建立MRS数据表.py`可能是一个脚本,用于根据...
- **新式类**:遵循广度优先原则,即按照MRO(Method Resolution Order,方法解析顺序)来进行查找。 **3. 特性** - 在Python 3中,不再区分经典类和新式类,所有的类都被认为是新式类,并且默认继承自`object`类。...
7. **高级Python特性**:了解Python的MRO算法,混合模式(Mixin)的使用,以及动态属性和特性属性的实现。深入理解元类和装饰器,它们在实现高级功能如缓存、日志记录、性能统计等方面非常有用。 8. **并发编程与...
在经典的旧式类中,Python 2.1采用DFS算法处理多继承的MRO。例如,有一个菱形继承结构,A、B、C三个类,B和C都继承自A,而D同时继承自B和C。当在D类中调用`who_am_i`方法时,经典类会首先查找D类,然后按顺序查找B和...
Python的MRO可以通过`__mro__`属性查看,也可以使用`mro()`方法: ```python print(ChildClass.__mro__) # 输出MRO列表 ``` 在实际应用中,多重继承可能会带来一些问题,比如“菱形问题”( Diamond Problem),即...
python 零基础学习篇
默认情况下,Python 使用 C3 算法来确定 MRO,它保证了调用顺序的线性一致性。 总的来说,类的继承是 Python 面向对象编程中一个重要的特性,它使得代码的组织更加模块化,提高了代码的可读性和可维护性。通过继承...