python super的使用方法及实例详解


Posted in Python onSeptember 25, 2019

功能

super功能:super函数是子类用于调用父类(超类)的一个方法。

用法

1.在子类 __init__() 方法中正确的初始化父类,保证相同的基类只初始化一次。

2.覆盖特殊方法。

3.解决多重继承中,子类重复调用父类方法的问题。

注意

super()继承只能用于新式类,用于经典类时就会报错。

新式类:必须有继承的类,如果无继承的,则继承object

经典类:没有父类,如果此时调用super就会出现错误:『super() argument 1 must be type, not classobj)

在子类__init__()方法中正确初始化父类,保证相同的基类只初始化一次

假如说在父类中实现了一个方法,你想在子类中使用父类的这个方法并且做一定扩展但是又不想完全重写,并且这个场景中的继承属于多继承,那么super()就出场了,可以实现方法的增量修改。

A(父类)有x属性,B(子类)想添加y属性:

class A(object):
  def __init__(self,x):
    self.x = x
class B(A):
  def __init__(self,x,y):
    super(B,self,).__init__(x)
    self.y = y
a = A(2)
b = B(2,4)
print(a.x)
print(b.x,b.y)

覆盖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() 仍然可以有效的工作。

解决多重继承中,子类重复调用父类方法的问题

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__() 的原因。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python实现根据指定端口探测服务器/模块部署的方法
Aug 25 Python
使用Python编写Linux系统守护进程实例
Feb 03 Python
Python中的多重装饰器
Apr 11 Python
Python多进程并发(multiprocessing)用法实例详解
Jun 02 Python
用pickle存储Python的原生对象方法
Apr 28 Python
python爬虫框架talonspider简单介绍
Jun 09 Python
Python反射用法实例简析
Dec 22 Python
使用django-crontab实现定时任务的示例
Feb 26 Python
python图形开发GUI库wxpython使用方法详解
Feb 14 Python
Python学习之os模块及用法
Jun 03 Python
selenium如何定位span元素的实现
Jan 13 Python
selenium3.0+python之环境搭建的方法步骤
Feb 01 Python
Pycharm+Python+PyQt5使用详解
Sep 25 #Python
利用python、tensorflow、opencv、pyqt5实现人脸实时签到系统
Sep 25 #Python
Python 3.6 中使用pdfminer解析pdf文件的实现
Sep 25 #Python
Python实现串口通信(pyserial)过程解析
Sep 25 #Python
Python根据服务获取端口号的方法
Sep 25 #Python
Python提取PDF内容的方法(文本、图像、线条等)
Sep 25 #Python
python使用 request 发送表单数据操作示例
Sep 25 #Python
You might like
DEDE采集大师官方留后门的删除办法
2011/01/08 PHP
php使用APC实现实时上传进度条功能
2015/10/26 PHP
JS 树形递归实例代码
2010/05/18 Javascript
一个页面元素appendchild追加到另一个页面元素的问题
2013/01/27 Javascript
jquery validate在ie8下的bug解决方法
2013/11/13 Javascript
javascript通过获取html标签属性class实现多选项卡的方法
2015/07/27 Javascript
简单理解JavaScript中的封装与继承特性
2016/03/19 Javascript
NodeJS处理Express中异步错误
2017/03/26 NodeJs
js 用于检测类数组对象的函数方法
2017/05/02 Javascript
基于js中document.cookie全面解析
2017/09/14 Javascript
AngularJS 的$timeout服务示例代码
2017/09/21 Javascript
微信小程序列表渲染功能之列表下拉刷新及上拉加载的实现方法分析
2017/11/27 Javascript
Vue2 轮播图slide组件实例代码
2018/05/31 Javascript
详解Webpack多环境代码打包的方法
2018/08/03 Javascript
js实现图片放大并跟随鼠标移动特效
2019/01/18 Javascript
如何使用CSS3+JQuery实现悬浮墙式菜单
2019/06/18 jQuery
Vue数据双向绑定底层实现原理
2019/11/22 Javascript
vue的hash值原理也是table切换实例代码
2020/12/14 Vue.js
[49:56]VG vs Optic 2018国际邀请赛小组赛BO2 第一场 8.19
2018/08/21 DOTA
[51:15]完美世界DOTA2联赛PWL S2 PXG vs Magma 第一场 11.21
2020/11/24 DOTA
python里对list中的整数求平均并排序
2014/09/12 Python
使用Python的Flask框架来搭建第一个Web应用程序
2016/06/04 Python
Python中使用多进程来实现并行处理的方法小结
2017/08/09 Python
Django框架基础模板标签与filter使用方法详解
2019/07/23 Python
Django自定义模板过滤器和标签的实现方法
2019/08/21 Python
tornado+celery的简单使用详解
2019/12/21 Python
使用Python画了一棵圣诞树的实例代码
2020/11/27 Python
法国在线购买汽车轮胎网站:123pneus.fr
2019/02/25 全球购物
经典c++面试题四
2015/05/14 面试题
当我正在为表建立索引的时候,SQL Server 会禁止对表的访问吗
2014/04/28 面试题
UNIX文件类型
2013/08/29 面试题
教学器材管理制度
2014/01/26 职场文书
邀请函模板
2015/02/02 职场文书
2019销售早会主持词
2019/06/27 职场文书
golang switch语句的灵活写法介绍
2021/05/06 Golang
Redis高可用集群redis-cluster详解
2022/03/20 Redis