文档章节

Python的super解释

shawnplaying
 shawnplaying
发布于 2016/08/10 10:47
字数 1218
阅读 14
收藏 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
粉丝 15
博文 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
2018/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
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
2018/07/22
0
0
python3的多级继承失效排查

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

漫步海边小路
2018/09/12
0
0
学习笔记(11月06日) --类

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

wanyang_wanyang
2018/06/26
0
0

没有更多内容

加载失败,请刷新页面

加载更多

mysql 查询当天、本周,本月,上一个月的数据

今天 select * from 表名 where to_days(时间字段名) = to_days(now()); 昨天 SELECT * FROM 表名 WHERE TO_DAYS( NOW( ) ) - TO_DAYS( 时间字段名) <= 1 近7天 SELECT * FROM 表名 wher......

BraveLN
40分钟前
2
0
Android Multimedia框架总结(六)C++中MediaPlayer的C/S架构

前面几节中,都是通过java层调用到jni中,jni向下到c++层并未介绍 看下Java层一个方法在c++层 MediaPlayer后续过程 frameworks/av/media/libmedia/MediaPlayer.cpp 找一个我们之前熟悉的setDa...

天王盖地虎626
58分钟前
2
0
【Linux】【MySQL】CentOS7安装最新版MySQL8.0.13(最新版MySQL从安装到运行)

1、前言   框框博客在线报时:2018-11-07 19:31:06   当前MySQL最新版本:8.0.13 (听说比5.7快2倍)   官方之前表示:MySQL 8.0 正式版 8.0.11 已发布,MySQL 8 要比 MySQL 5.7 快 2 ...

Code辉
58分钟前
1
0
oracle dg备库重建redolog:ora-00313,ora-00312

trace文件: Errors in file /crbank/dbs/app/product/diag/rdbms/rdbs/dbs/trace/dbs_mrp0_24445130.trc: ORA-00313: open failed for members of log group 8 of thread 1 ORA-00312: onl......

hnairdb
今天
1
0
深入分析Java I/O的工作机制 (一)

1.Java的I/O类库的基本架构 先说一下什么是类库:可以说是类的集合,类库包括接口、抽象类、具体类等。 I/O是机器获取和交互信息的主要渠道。 java在I/O上也一直在做持续的优化,在1.4版开始...

java菜分享
今天
8
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部