python学习笔记(22)--类的详解4-多重继承(super()函数)
来源:互联网 发布:兄弟连高洛峰 php教程 编辑:程序博客网 时间:2024/05/18 01:42
说到面向对象,就少不了研究面向对象的特点(继承,封装,多态)。Python中类的继承的关键是正确使用super()函数,而这恰好是我们理解最不好的地方。先看看一般类的继承的代码(关于我写的类的详解1就是这么写,现在觉得写法实在比较粗糙):
class Base: def __init__(self): print('This is Base init function')class A(Base): def __init__(self): Base.__init__(self) print('This is init function of A')class B(Base): def __init__(self): Base.__init__(self) print('This is init function of B')class C(A,B): def __init__(self): #super().__init__(self) A.__init__(self) B.__init__(self) print('This is init function of C')c = C()控制台输出:This is Base init functionThis is init function of AThis is Base init functionThis is init function of BThis is init function of C
从打印输出来看,初始化确实也没有什么问题。只是Base()被初始化两次,并不影响其功能,只是程序员接受不了。接下来用super()函数重写该段代码:
class Base: def __init__(self): print('This is Base init function')class A(Base): def __init__(self): super().__init__() print('This is init function of A')class B(Base): def __init__(self): super().__init__() print('This is init function of B')class C(A,B): def __init__(self): super().__init__() #A.__init__(self) #B.__init__(self) print('This is init function of C')c = C()打印输出:This is Base init functionThis is init function of BThis is init function of AThis is init function of C
用super()函数重写之后,Base的初始化函数就只运行了一次。
要理解为什么会这样,我们得先去理解python是如何实现类的继承的。针对于每一个定义的类,python都会计算出一个方法解析顺序(MRO)的列表。MRO列表只是简单地对所有的基类进行线性排列:
print(C.__mro__)print(type(C.__mro__))(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>)<class 'tuple'>
可以看出,MRO是以元组的结构来储存的,同时,要实现继承,python从MRO最左边的类开始(子类),从左到右依次查找,直到查找到待查的属性为止。
MRO列表是如何确定的呢,python采用的是C3线性化处理(C3 linearization)的技术。简单来说就是一种针对父类的归并排序它满足3个条件:
(1)先检查子类,再检查父类
(2)有多个父类(多重继承),按照MRO列表的顺序依次检查
(3)如果下一个类中出现两个合法的选择,那么就从第一个父类中选择(避免重复继承,保证每个父类只继承一次)
当使用super()函数时,python会继续从MRO中的下一个类开始搜索,只要每一个重新定义过的方法(比如init())都使用了super()函数,并且调用了他们一次,那么控制流最终就可以遍历整个MRO列表,并且让每个方法都只被调用一次(这就是第二个例子中为什么Base.init()只被调用一次的原因)。
关于super()函数,还有一个很神奇的功能:它并不是一定要关联到某个类的直接父类上,甚至可以在没有直接父类的类上使用它。例如:
class A: def __init__(self): print('This is init function of A')class B: def __init__(self): print('This is init function of B') super().__init__()class C(B,A): passc = C()打印输出:This is init function of BThis is init function of A
那么问题来了,A并不是B的父类,但是B中使用super函数仍然可以调用A中的init()。这个就可以利用C的MRO列表来解释:
(<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
B中使用了super()它就会遍历MRO,寻找下一个方法,在A中找到了,所以就调用了它。
这就说明,在使用super()函数时可能会产生我们并不想要的结果,所以我们要遵守一些基本规则来避免这些情况的发生。例如:确保在继承体系中所有同名的方法都有可兼容的调用签名(参数数量相同,参数名称也相同,则只会调用一个)
- python学习笔记(22)--类的详解4-多重继承(super()函数)
- python 27 super继承(解决多重继承时,老办法init父类多次的问题)
- python 27 super继承(解决多重继承时,老办法init父类多次的问题)
- Python的多重继承和super
- python学习笔记 多重继承
- Python学习笔记(5)-多重继承的坑
- Python super继承详解
- Python学习笔记(4)Python中super的用法
- Python学习笔记(4)Python中super的用法
- phton3.4.1类多重继承&父类函数的调用&super用法
- C++学习笔记(6)——多重继承类对象的构造函数参数的传递方法
- python学习-多重继承
- python学习——super()方法实现类的继承
- super()与继承的学习笔记
- Python 在子类中调用父类方法详解(单继承、多层继承、多重继承)
- C++学习笔记--多重继承的问题
- python的多重继承
- python的多重继承
- 链表----链表原理
- 学习maven命令笔记
- java数据结构与算法之树基本概念及二叉树(BinaryTree)的设计与实现
- html定位position
- 彷QQ消息侧滑删除置顶
- python学习笔记(22)--类的详解4-多重继承(super()函数)
- 什么是线程安全
- 已解决:登录Linux后好多命令不能用,如使用service会报错service command not found
- CCF题目java实现
- Android 网络操作
- svn启动
- css3.0新增属性
- Verilog HDL 学习(一)
- python中decode和encode的区别