文档章节

Python的super解释

shawnplaying
 shawnplaying
发布于 2016/08/10 10:47
字数 1218
阅读 13
收藏 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
学习笔记(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
python3的多级继承失效排查

假设有下面的代码: 正常应该显示: 但实际跳过了几个类,显示: 经过排查发现在 D 中使用了下面的语句: 指定了父类,从而直接跳过了 C 和 B. 当然实际代码比这个要复杂,所以在一定程度上隐藏了问...

漫步海边小路
09/12
0
0

没有更多内容

加载失败,请刷新页面

加载更多

linux-scp 远程拷贝报错原因

刚拿到一台重装后的服务器,远程ssh都正常,但是一scp拷贝东西就报错: 本地确定是有scp命令的,而且如果是本地没有scp不会报后面那句lost connection,因此就是远程没有scp这个命令。因此在...

linuxprobe16
22分钟前
1
0
OSChina 周六乱弹 —— 谁小时候没当过熊孩子呀

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @小小编辑:推荐歌曲《行尸走肉》- amazarashi 《行尸走肉》- amazarashi 手机党少年们想听歌,请使劲儿戳(这里) @神话 :周五了,周末干啥...

小小编辑
46分钟前
31
1
docker部署springboot项目

安装docker 菜鸟教程 springboot项目 maven依赖 <?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001......

yimingkeji
今天
14
0
1: Cordova 配置WebView可以打开外部链接

一、问题:在使用Cordova生成的Android App中默认情况下WebView中的超链接,如果不是相对链接,会默认使用浏览器打开。 如果想用默认webview打开 解决方案:修改config.xml文件添加链接配置节...

wecloudnet
今天
1
0
Beetl介绍以及集成SpringBoot2.0 ---《Beetl视频课程》(1)

目的:引导阅读官方文档 目标:实现一个自己的博客 一、Beetl介绍 Beetl目前版本是2.9.3,相对于其他java模板引擎,具有功能齐全,语法直观,性能超高,以及编写的模板容易维护等特点。使得开发...

Gavin-King
今天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部