文档章节

Python的super解释

shawnplaying
 shawnplaying
发布于 2016/08/10 10:47
字数 1218
阅读 11
收藏 0
点赞 0
评论 0

在子类中初始化父类, 传统的方式是在子类中直接调用父类的__init__函数:

class MyBaseClass(object):
    def __init__(self, value):
        self.value = value

class MyChildClass(MyBaseClass):
    def __init__(self):
        MyBaseClass.__init__(self, 5)

在继承体系很简单的情况下, 这种方式可以正常运行, 但是在大多数情况下这种方法都是不可行的. 如果一个类继承自多个类, 那么直接调用父类的__init__函数就会产生不可预知的结果.

问题之一是, 在子类里调用__init__的顺序并不固定. 例如定义两个父类:

class TimesTwo(object):
    def __init__(self):
        self.value *= 2

class PlusFive(object):
    def __init__(self):
        self.value += 5

下面这个类以其中一种顺序继承上述两个类:

class OneWay(MyBaseClass, TimesTwo, PlusFive):
    def __init__(self, value):
        MyBaseClass.__init__(self, value)
        TimesTwo.__init__(self)
        PlusFive.__init__(self)

构建一个实例, 其产生的结果与继承的顺序是一致的:

foo = OneWay(5)
print('First ordering is (5 * 2) + 5 =', foo.value)

>>>
First ordering is (5 * 2) + 5 = 15

下面这个类以另外一种顺序继承两个父类:

class AnotherWay(MyBaseClass, PlusFive, TimesTwo):
    def __init__(self, value):
        MyBaseClass.__init__(self, value)
        TimesTwo.__init__(self)
        PlusFive.__init__(self)

但是在构造函数中调用父类__init__方法的顺序没有改变, 这就导致了该类所产生的结果与其继承顺序不一致:

bar = AnotherWay(5)
print('Second ordering still is', bar.value)

>>>
Second ordering still is 15

另一种问题出现在菱形继承(diamond inheritance)中. 菱形继承是指一个类继承自两个不同的类, 而这两个类有一个共同父类. 在菱形继承中, 公共父类的__init__构造方法会运行多次, 从而导致意料之外的结果. 例如, 以下两个类都继承自MyBaseClass:

class TimesFive(MyBaseClass):
    def __init__(self, value):
        MyBaseClass.__init__(self, value)
        self.value *= 5

class PlusTwo(MyBaseClass):
    def __init__(self, value):
        MyBaseClass.__init__(self, value)
        self.value += 2

以下这个类继承自上述两个类, 这就使得MyBaseClass成了菱形继承中的顶端类:

class ThisWay(TimesFive, PlusTwo):
    def __init__(self, value):
        TimesFive.__init__(self, value)
        PlusTwo.__init__(self, value)

我们来实例化这个类看一下效果:

foo = ThisWay(5)
print('Should be (5 * 5) + 2 = 27 but is', foo.value)
>>>
Should be (5 * 5) + 2 = 27 but is 7

我们本来想要的结果是(5 * 5) + 2 = 27, 但是调用第二个父类PlusTwo的构造方法PlusTwo.__init__时, 第二次调用了顶端父类MyBaseClass的构造方法MyBaseClass.__init__, 使得self.value又变成了5.

为了解决这个问题, Python2.2添加了一个名为super的内置函数, 并定义了方法解析顺序(Method Resolution Order, MRO). MRO以标准的流程来安排超类之间的初始化顺序, 也保证了菱形继承中顶端类的__init__方法只会运行一次.

以下代码使用Python2风格的super方法定义了菱形继承体系:

# Python2
class TimesFiveCorrect(MyBaseClass):
    def __init__(self, value):
        super(TimesFiveCorrect, self).__init__(value)
        self.value *= 5

class PlusTwoCorrect(MyBaseClass):
    def __init__(self, value):
        super(PlusTwoCorrect, self).__init__(value)
        self.value += 2

现在, 顶端类的构造方法MyBaseClass.__init__只会运行一次, 而其他超类的初始化顺序, 则与这些超类在class语句中的出现顺序相同:

# Python 2
class GoodWay(TimesFiveCorrect, PlusTwoCorrect):
    def __init__(self, value):
        super(GoodWay, self).__init__(value)

foo = GoodWay(5)
print 'Should be 5 * (5 + 2) = 35 and is', foo.value
>>>
Should be 5 * (5 + 2) = 35 and is 35

我们可能会认为应该是先*5, 然后+2得出结果是27才对. 但实际上, 程序的运行顺序和GoodWay类的MRO保持一致, 这个MRO顺序可以通过名为mro的方法来查询:

from pprint import pprint
pprint(GoodWay.mro())
>>>
[<class '__main__.GoodWay'>,
<class '__main__.TimesFiveCorrect'>,
<class '__main__.PlusTwoCorrect'>,
<class '__main__.MyBaseClass'>,
<class 'object'>]

调用GoodWay(5)时, 它会调用TimesFiveCorrect.__init__, 而TimesFiveCorrect.__init__又会调用PlusTwoCorrect.__init__, 接着PlusTwoCorrect.__init__调用MyBaseClass.__init__, 到达菱形顶部后, 所有的构造方法会按照与刚才那些__init__相反的顺序来执行, 于是MyBaseClass.__init__会把value设为5, 然后PlusTwoCorrect.__init__会为其加2, 变成7, 最后TimesFiveCorrect.__init__将其乘以5, 结果就变为35.

在Python2中有这样两个问题:

  1. super语句有些麻烦, 必须指定所在的类, self对象, 构造方法名(通常是__init__)以及所有的参数值.

  2. 调用super时, 必须写出当前类的名称, 但是类的名称很可能会更改, 那么每一条super语句也就必须要更改.

在Python3中则没有这些问题, 它提供了一种不带参数的super调用方式:

class Explicit(MyBaseClass):
    def __init__(self, value):
        super(__class__, self).__init__(value * 2)

class Implicit(MyBaseClass):
    def __init__(self, value):
        super().__init__(value * 2)

assert Explicit(10).value == Implicit(10).value

小结

  1. Python使用MRO来解决父类初始化顺序及菱形继承问题;

  2. 应该使用内置的super函数来初始化父类.

参考资料:

  1. Slatkin, Brett. Effective Python: 59 Specific Ways to Write Better Python. Pearson Education, 2015.

  2. Brett Slatkin, 爱飞翔. Effective Python: 编写高质量Python代码的59个有效 方法. 机械工业出版社, 2016.1.

本文转载自:http://mp.weixin.qq.com/s?src=3×tamp=1470794975&ver=1&signature=CRR*xs7ZU0SKpbu2di3WYTK-u3zGW9H

共有 人打赏支持
shawnplaying
粉丝 14
博文 128
码字总数 70642
作品 0
海淀
系统管理员
学习笔记(11月07日)--类

四周二次课(11月7日) 一、 类的重写 1.1 重写一般方法 class A: def hello(self): print('Hello,i am A.')class B(A): passa = A()b = B()a.hello()b.hello() 结果: Hello,i am A.Hello,i......

wanyang_wanyang
07/03
0
0
使用SIP对C++类进行Python封装

本文来自于: http://pyqt.sourceforge.net/Docs/sip4/using.html#a-simple-c-example 本人翻译,欢迎转载,赠人玫瑰,手留余香。 Using SIP Bindings are generated by the SIP code generat......

openthings
2015/02/06
0
0
python3.x与python2.x的区别汇总

python3.x与python2.7.x都是比较流行的版本,虽然建议现在的初学者开始学习python3.x的版本,但是还有很多的工程使用的是python2.7.x版本。观看代码的时候难免会出现一些问题。 在google上搜...

oldpan
2017/10/10
0
0
学习笔记(11月06日) --类

四周一次课(11月6日) 一、类的一般形式 创建类我们一般用class关键字来创建一个类,class后面跟类名字,可以自定义,最后以冒号结尾,如下所示: class ClassName: '''类的说明''' 类的内容...

wanyang_wanyang
06/26
0
0
王老板Python面试(10):17道python笔试面试真题

1、一行代码实现1--100之和 利用sum()函数求和 2、如何在一个函数内部修改全局变量 利用global 修改全局变量 3、列出5个python标准库 os:提供了不少与操作系统相关联的函数 sys: 通常用于命...

程序员八阿哥
05/22
0
0
python2与python3共存的解决方案

作者:匿名用户 链接:https://www.zhihu.com/question/21653286/answer/95532074 来源:知乎 想学习Python3,但是暂时又离不开Python2。在Windows上如何让它们共存呢?目前国内网站经常会让...

USTC_HCH
06/26
0
0
学习笔记(11月13日)--json的使用

五周一次课(11月13日) 一、json的使用 Json简介:Json,全名 JavaScript Object Notation,是一种轻量级的数据交换格式。Json最广泛的应用是作为AJAX中web服务器和客户端的通讯的数据格式。...

wanyang_wanyang
06/26
0
0
笨办法学 Python · 续 练习 35:解释器

练习 35:解释器 原文:Exercise 35: Interpreters 译者:飞龙 协议:CC BY-NC-SA 4.0 自豪地采用谷歌翻译 解析中的最后一个练习应该既具有挑战性又有趣。你终于可以看到,你的微型 Python 脚...

apachecn_飞龙
2017/08/13
0
0
不懂Python,你将成为人工智能时代的新“文盲”

  每个阶段,大家对“文盲”的定义都是不相同的,以前不识汉字就是文盲,后来不会说英语就是文盲。在人工智能时代的今天,当然不懂Python语言,你就是“文盲”!现在你肯定在问Why?   众...

深度学习
06/12
0
0
1.2.3 Python简介和优势

Python 由 Guido 于1989年年底开发,Python 语言是基于ABC教学语言的。ABC这种语言非常优美和强大,是专门为非专业程序员设计的。但是,ABC语言并没有获得广泛的应用,Guido认为是非开放造成...

Gooiem
2015/08/15
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

防火墙实例

3、一个包过滤防火墙实例 环境:redhat9 加载了string time等模块 eth0 接外网──ppp0 eth1 接内网──192.168.0.0/24 #!/bin/sh modprobe ipt_MASQUERADE modprobe ip_conntrack_ftp modp...

李超小牛子
3分钟前
0
0
TensorFlow 作用域与操作符的受限范围

variable_scope 影响变量和操作符 name_scope 只影响操作符 with tf.name_scope(""),使用空字符串将作用域返回到顶层 tf.variable_scope("") 相当于添加一个空层 import tensorflow as tf...

阿豪boy
14分钟前
0
0
Java面试基础篇——第六篇:常见Map类的区别

常见的map类有: HashMap, ConcurrentHashMap (Jdk1.8) , LinkedHashMap, TreeMap, Hashtable。 其中我们最常用的莫过于HashMap, 和并发情况下使用的ConcurrentHashMap了,它们的主要区别就在...

developlee的潇洒人生
15分钟前
0
0
崛起于Springboot2.X之前端模版freemaker(23)

1、配置文件 spring: freemarker: allow-request-override: false cache: true check-template-location: true charset: UTF-8 content-type: text/html ......

木九天
32分钟前
1
0
spring-boot:run启动时,指定spring.profiles.active

Maven启动指定Profile通过-P,如mvn spring-boot:run -Ptest,但这是Maven的Profile。 如果要指定spring-boot的spring.profiles.active,则必须使用mvn spring-boot:run -Drun.profiles=test......

夜黑人模糊灬
34分钟前
0
0
大数据分析挖掘技术学习:Python文本分类

引言 文本分类作为自然语言处理任务之一,被广泛应用于解决各种商业领域的问题。文本分类的目的是将 文本/文档 自动地归类为一种或多种预定义的类别。常见的文本分类应用如下: • 理解社交媒...

加米谷大数据
38分钟前
0
0
istio-0.8 指标监控,prometheus,grafana

配置: https://istio.io/docs/tasks/telemetry/metrics-logs/ https://istio.io/docs/tasks/telemetry/tcp-metrics/ envoy拦截请求>上报mixer>对接prometheus>grafana 效果截图: promethe......

xiaomin0322
40分钟前
0
0
公众号推荐

阿里技术 书籍:《不止代码》

courtzjl
43分钟前
0
0
关于改进工作效率

1.给不同的业务线建立需求群,所有的数据需求都在群里面提。 2.对于特别难搞定的事情,到对应的技术哪去做,有问题随时沟通。 3.定期给工作总结形成方法论。 4.学习新的技术,尝试用新的方法...

Avner
50分钟前
0
0
关于thinkphp 框架开启路径重写,无法获取Authorization Header

今天遇到在thinkphp框架中获取不到header头里边的 Authorization ,后来在.htaccess里面加多一项解决,记录下: <IfModule mod_rewrite.c> Options +FollowSymlinks -Multiviews Rewrite......

殘留回憶
53分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部