python3 推荐使用super调用base类方法
from:https://python3-cookbook.readthedocs.io/zh_CN/latest/c08/p07_calling_method_on_parent_class.html
8.7 调用父类方法
问题
你想在子类中调用父类的某个已经被覆盖的方法。
解决方案
为了调用父类(超类)的一个方法,可以使用 super() 函数,比如:
class A:
def spam(self):
print('A.spam') class B(A):
def spam(self):
print('B.spam')
super().spam() # Call parent spam()
super() 函数的一个常见用法是在 __init__() 方法中确保父类被正确的初始化了:
class A:
def __init__(self):
self.x = 0 class B(A):
def __init__(self):
super().__init__()
self.y = 1
super() 的另外一个常见用法出现在覆盖Python特殊方法的代码中,比如:
class Proxy:
def __init__(self, obj):
self._obj = obj # Delegate attribute lookup to internal obj
def __getattr__(self, name):
return getattr(self._obj, name) # Delegate attribute assignment
def __setattr__(self, name, value):
if name.startswith('_'):
super().__setattr__(name, value) # Call original __setattr__
else:
setattr(self._obj, name, value)
在上面代码中,__setattr__() 的实现包含一个名字检查。 如果某个属性名以下划线(_)开头,就通过 super() 调用原始的 __setattr__() , 否则的话就委派给内部的代理对象 self._obj 去处理。 这看上去有点意思,因为就算没有显式的指明某个类的父类, super() 仍然可以有效的工作。
讨论
实际上,大家对于在Python中如何正确使用 super() 函数普遍知之甚少。 你有时候会看到像下面这样直接调用父类的一个方法:
class Base:
def __init__(self):
print('Base.__init__') class A(Base):
def __init__(self):
Base.__init__(self)
print('A.__init__')
尽管对于大部分代码而言这么做没什么问题,但是在更复杂的涉及到多继承的代码中就有可能导致很奇怪的问题发生。 比如,考虑如下的情况:
class Base:
def __init__(self):
print('Base.__init__') class A(Base):
def __init__(self):
Base.__init__(self)
print('A.__init__') class B(Base):
def __init__(self):
Base.__init__(self)
print('B.__init__') class C(A,B):
def __init__(self):
A.__init__(self)
B.__init__(self)
print('C.__init__')
如果你运行这段代码就会发现 Base.__init__() 被调用两次,如下所示:
>>> c = C()
Base.__init__
A.__init__
Base.__init__
B.__init__
C.__init__
>>>
可能两次调用 Base.__init__() 没什么坏处,但有时候却不是。 另一方面,假设你在代码中换成使用 super() ,结果就很完美了:
class Base:
def __init__(self):
print('Base.__init__') class A(Base):
def __init__(self):
super().__init__()
print('A.__init__') class B(Base):
def __init__(self):
super().__init__()
print('B.__init__') class C(A,B):
def __init__(self):
super().__init__() # Only one call to super() here
print('C.__init__')
运行这个新版本后,你会发现每个 __init__() 方法只会被调用一次了:
>>> c = C()
Base.__init__
B.__init__
A.__init__
C.__init__
>>>
为了弄清它的原理,我们需要花点时间解释下Python是如何实现继承的。 对于你定义的每一个类,Python会计算出一个所谓的方法解析顺序(MRO)列表。 这个MRO列表就是一个简单的所有基类的线性顺序表。例如:
>>> C.__mro__
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>,
<class '__main__.Base'>, <class 'object'>)
>>>
为了实现继承,Python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
而这个MRO列表的构造是通过一个C3线性化算法来实现的。 我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
- 子类会先于父类被检查
- 多个父类会根据它们在列表中的顺序被检查
- 如果对下一个类存在两个合法的选择,选择第一个父类
老实说,你所要知道的就是MRO列表中的类顺序会让你定义的任意类层级关系变得有意义。
当你使用 super() 函数时,Python会在MRO列表上继续搜索下一个类。 只要每个重定义的方法统一使用 super() 并只调用它一次, 那么控制流最终会遍历完整个MRO列表,每个方法也只会被调用一次。 这也是为什么在第二个例子中你不会调用两次 Base.__init__() 的原因。
super() 有个令人吃惊的地方是它并不一定去查找某个类在MRO中下一个直接父类, 你甚至可以在一个没有直接父类的类中使用它。例如,考虑如下这个类:
class A:
def spam(self):
print('A.spam')
super().spam()
如果你试着直接使用这个类就会出错:
>>> a = A()
>>> a.spam()
A.spam
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in spam
AttributeError: 'super' object has no attribute 'spam'
>>>
但是,如果你使用多继承的话看看会发生什么:
>>> class B:
... def spam(self):
... print('B.spam')
...
>>> class C(A,B):
... pass
...
>>> c = C()
>>> c.spam()
A.spam
B.spam
>>>
你可以看到在类A中使用 super().spam() 实际上调用的是跟类A毫无关系的类B中的 spam() 方法。 这个用类C的MRO列表就可以完全解释清楚了:
>>> C.__mro__
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>,
<class 'object'>)
>>>
在定义混入类的时候这样使用 super() 是很普遍的。可以参考8.13和8.18小节。
然而,由于 super() 可能会调用不是你想要的方法,你应该遵循一些通用原则。 首先,确保在继承体系中所有相同名字的方法拥有可兼容的参数签名(比如相同的参数个数和参数名称)。 这样可以确保 super() 调用一个非直接父类方法时不会出错。 其次,最好确保最顶层的类提供了这个方法的实现,这样的话在MRO上面的查找链肯定可以找到某个确定的方法。
在Python社区中对于 super() 的使用有时候会引来一些争议。 尽管如此,如果一切顺利的话,你应该在你最新代码中使用它。 Raymond Hettinger为此写了一篇非常好的文章 “Python’s super() Considered Super!” , 通过大量的例子向我们解释了为什么 super() 是极好的。
python3 推荐使用super调用base类方法的更多相关文章
- Unity调用Android类方法
Unity调用Android类方法 1. 添加Unity的classes.jar文件 创建一个Android工程AndroidUnityDemo. 由于Unity的版本不同,直接在Unity安装包文 ...
- java构造函数是否可继承,以及子类构造函数可否不使用super调用超类构造函数
问题一:java的构造函数能否被继承? 笔者初学java看的一本书说:“java的子类自然的继承其超类的“非private成员”. 通常java的构造函数被设置为public的(若你不写构造函数,ja ...
- 继承内部类时使用外部类对象.super()调用内部类的构造方法
问题简介 今天在看<Java编程思想>的时候,看到了一个很特殊的语法,懵逼了半天--一个派生类继承自一个内部类,想要创建这个派生类的对象,首先得创建其父类的对象,也就是这个内部类,而调 ...
- Python3中的super()函数详解
关于Python3中的super()函数 我们都知道,在Python3中子类在继承父类的时候,当子类中的方法与父类中的方法重名时,子类中的方法会覆盖父类中的方法, 那么,如果我们想实现同时调用父类和子 ...
- super()调用父类构造方法
super()表示调用父类中的构造方法 1.子类继承父类,子类的构造方法的第一行,系统会默认编写super(),在调用子类的构造方法时,先调用父类的无参数构造方法 2.如果父类中只有有参数构造方法,那 ...
- 使用super调用父类的构造方法
package com.bjpowernode.t02inheritance.c09; /* * 使用super调用父类的构造方法 */public class TestSuper02 { publi ...
- python使用super()调用父类的方法
如果要在子类中引用父类的方法,但是又需要添加一些子类所特有的内容,可通过类名.方法()和super()来调用父类的方法,再个性化子类的对应函数. 直接使用类名.方法()来调用时,还是需要传入self为 ...
- 关于父类私有属性在子类构造函数中super调用的解释
package test; public class Car { private int carMoney; //汽车租金 private String carName; //汽车名字 private ...
- Python3.x:pyodbc调用sybase的存储过程
Python3.x:pyodbc调用sybase的存储过程 示例代码 # python3 # author lizm # datetime 2018-03-02 17:00:00 # -*- codi ...
随机推荐
- 值类型前加ref和out的区别
1.值类型前加ref,在调用前必须先初始化,初始化之后在方法内部直接使用 值类型x前加了ref,方法外的x会随着方法内的x改变而改变,因为此时传的是地址,如下面的例子, x前加了ref所以x = x+ ...
- [转帖]腾讯将使用AMD第二代霄龙处理器打造自研服务器:性能提升35%
腾讯将使用AMD第二代霄龙处理器打造自研服务器:性能提升35% https://news.cnblogs.com/n/647499/ 我司的服务器是不是要少一块蛋糕了.. 作者:万南 今日,AMD 宣 ...
- Asp.net Core CORS 跨域
本文主要介绍在Asp.net Core采用CORS方式解决跨域 关于跨域的原理介绍可参考Asp.net Web API 解决跨域详解 1 在Startup添加允许跨域的策略 services.AddC ...
- Select与Epoll的区别
相同点: 都是IO多路转接,都是一个线程能同一时间等待一堆描述符 不同点: 1.select接口使用不方便,每次调用完select都需要重新设置fd_set,因为输入输出未分离,返回的fd_set ...
- mysql 免费的图形管理工具
在学习go语言开发时,使用了mysql 使用了两天mysql命令行,感觉实在是无法忍受, 找到了一个免费好用的 图形数据库管理工具SQLyog Professional 版本: 注册名:luoye25 ...
- My Swift Study
参考资源 <swifter> https://github.com/iOS-Swift-Developers/Swift 闭包逃逸 swift3中,闭包默认是非逃逸的.如果一个函数参数可能 ...
- 在 WPF 中获取一个依赖对象的所有依赖项属性
原文:在 WPF 中获取一个依赖对象的所有依赖项属性 本文介绍如何在 WPF 中获取一个依赖对象的所有依赖项属性. 本文内容 通过 WPF 标记获取 通过设计器专用方法获取 通过 WPF 标记获取 p ...
- 未检测到.NET CORE SDK 或者 新建项目没有.NET CORE 3.0选择项
终于解决了 首先先看自己的VS2019版本 由于楼主下载的 .NET CORE SDK 3.0.100-preview8-013656 焕然大悟 原来是版本不符合,需要用vs 2019 preview ...
- 通俗易懂的join、left join、right join、full join、cross join
内连接:列出与连接条件匹配的数据行(join\inner join) 外连接:两表合并,如有不相同的列,另外一个表显示null(left join\right join\full outer join ...
- Base64图片编码的使用
一.base64编码介绍 Base64是网络上最常见的用于传输8Bit字节代码的编码方式之一,Base64编码可用于在HTTP环境下传递较长的标识信息.采用Base64编码具有不可读性,即所编码的数据 ...