文档章节

Python的super解释

shawnplaying
 shawnplaying
发布于 2016/08/10 10:47
字数 1218
阅读 11
收藏 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
ideamark/breadbot

Breadbot Overview Breadbot is a simple and powerful chatbot Easy to deploy More than 1000,000 corpus Fast response No database Based on Python3 Support WeChat Have a try Downloa......

ideamark
07/22
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Delphi 常用API 函数(好多都没见过)

AdjustWindowRect 给定一种窗口样式,计算获得目标客户区矩形所需的窗口大小 AnyPopup 判断屏幕上是否存在任何弹出式窗口 ArrangeIconicWindows 排列一个父窗口的最小化子窗口 AttachThread...

dillonxiao
12分钟前
1
0
阿里云ubuntu配置Android开发环境编译Apk

1.命令行下载Android SDK $ wget https://dl.google.com/android/android-sdk_r24.4.1-linux.tgz $ tar zxvf android-sdk_r24.4.1-linux.tgz 2.列出可以现在的SDK ./android list sdk  -a 3......

SuShine
13分钟前
1
0
maven导出项目依赖的jar包

一、导出到默认目录 targed/dependency 从Maven项目中导出项目依赖的jar包:进入工程pom.xml 所在的目录下,执行如下命令: mvn dependency:copy-dependencies 二、导出到自定义目录中 在mav...

来来来来来
13分钟前
1
0
Win10下React Native环境安装教程及错误处理办法(实测)

https://blog.csdn.net/zhangatle/article/details/53289471 准备工作 注意:小米手机MIUI有坑,文末有解决方法 1 首先,你需要先安装Node.js并进行环境变量的配置,具体可以参考我的另一篇文...

james_laughing
13分钟前
0
0
IDEA2018 Mybatis plugin破解

IDEA2018 Mybatis plugin破解 Mybatis Plugin 一、Mybatis Plugin插件是什么 提供Mapper接口与配置文件中对应SQL的导航 编辑XML文件时自动补全 根据Mapper接口, 使用快捷键生成xml文件及SQL...

DemonsI
14分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部