python super 继承_python2 为解决钻石继承难题,super()的使用

1. python2 子类调用父类函数成员有2种方法:普通方法和super()方法

假设Base是基类

class Base(object):

def __init__(self):

print “Base init”则普通方法如下

class Leaf(Base):

def __init__(self):

Base.__init__(self)

print “Leaf init”super()方法如下

class Leaf(Base):

def __init__(self):

super(Leaf, self).__init__()

print “Leaf init”

两种方法,效果一致:

>>> leaf = Leaf()

Base init

Leaf init

2.使用普通方法遇到钻石继承时,会遇到难题(孙子辈子类继承自两个叔叔级的父类,爷爷辈的类会初始化两次)。图1

如果使用普通方法时,Leaf 类同时调用Medium1,Medium2时会初始化两次Base类。

class Base(object):

def __init__(self):

print “Base init”

class Medium1(Base):

def __init__(self):

Base.__init__(self)

print “Medium1 init”

class Medium2(Base):

def __init__(self):

Base.__init__(self)

print “Medium2 init”

class Leaf(Medium1, Medium2):

def __init__(self):

Medium1.__init__(self)

Medium2.__init__(self)

print “Leaf init”

当生成Leaf对象时,Base 会被初始化两次,结果如下:

>>> leaf = Leaf()

Base init

Medium1 init

Base init

Medium2 init

Leaf init

3. 各语言的解决方法

钻石继承中,父类被多次初始化是个非常难缠的问题,我们来看看其他各个语言是如何解决这个问题的:

3.1. C++

C++使用虚拟继承来解决钻石继承问题。

Medium1和Medium2虚拟继承Base。当生成Leaf对象时,Medium1和Medium2并不会自动调用虚拟基类Base的构造函数,而需要由Leaf的构造函数显式调用Base的构造函数。

3.2.Java

Java禁止使用多继承。

Java使用单继承+接口实现的方式来替代多继承,避免了钻石继承产生的各种问题。

3.3.Ruby

Ruby禁止使用多继承。

Ruby和Java一样只支持单继承,但它对多继承的替代方式和Java不同。Ruby使用Mixin的方式来替代,在当前类中mixin入其他模块,来做到代码的组装效果。

3.4. Python

Python和C++一样,支持多继承的语法。但Python的解决思路和C++完全不一样,Python是的用就是super

同样的继承 用super 重写 ,生成Leaf对象试一下。

>>> leaf = Leaf()

Base init

Medium2 init

Medium1 init

Leaf init

相比于普通写法,super完美解决钻石继承问题,并且代码更为简洁。

4. super的内核:mro

要理解super的原理,就要先了解mro。mro是method resolution order的缩写,表示了类继承体系中的成员解析顺序。

在python中,每个类都有一个mro的类方法。我们来看一下钻石继承中,Leaf类的mro是什么样子的:

>>> Leaf.mro()

[, , , , ]

可以看到mro方法返回的是一个祖先类的列表。Leaf的每个祖先都在其中出现一次,这也是super在父类中查找成员的顺序。

通过mro,python巧妙地将多继承的图结构,转变为list的顺序结构。super在继承体系中向上的查找过程,变成了在mro中向右的线性查找过程,任何类都只会被处理一次。

通过这个方法,python解决了多继承中的2大难题:

1. 查找顺序问题。从Leaf的mro顺序可以看出,如果Leaf类通过super来访问父类成员,那么Medium1的成员会在Medium2之前被首先访问到。如果Medium1和Medium2都没有找到,最后再到Base中查找。

2. 钻石继承的多次初始化问题。在mro的list中,Base类只出现了一次。事实上任何类都只会在mro list中出现一次。这就确保了super向上调用的过程中,任何祖先类的方法都只会被执行一次。

至于mro的生成算法,可以参考这篇wiki:C3 linearization

5. super的具体用法

我们首先来看一下python中的super文档

>>> help(super)

Help on class super in module __builtin__:

class super(object)

| super(type, obj) -> bound super object; requires isinstance(obj, type)

| super(type) -> unbound super object

| super(type, type2) -> bound super object; requires issubclass(type2, type)

帮助文档语焉不详,下面详细解析super中的第1和第3种用法

5.1. super(type, obj)

当我们在Leaf的__init__中写这样的super时:

class Leaf(Medium1, Medium2):

def __init__(self):

super(Leaf, self).__init__()

print “Leaf init”

super(Leaf, self).__init__()的意思是说:获取self所属类的mro, 也就是[Leaf, Medium1, Medium2, Base]

从mro中Leaf右边的一个类开始,依次寻找__init__函数。这里是从Medium1开始寻找

一旦找到,就把找到的__init__函数绑定到self对象,并返回

从这个执行流程可以看到,如果我们不想调用Medium1的__init__,而想要调用Medium2的__init__,那么super应该写成:super(Medium1, self)__init__()

5.2. super(type, type2)

在Base,Medium1,Medium2, Leaf类中分别写入_new_()方法,并使用super():

class Base(object):

def __new__(self):

print "Base new"

class Medium1(Base):

def __new__(cls):

super(Medium1,cls).__new__(cls)

print "Medium1 new"

class Medium2(Base):

def __new__(cls):

super(Medium2,cls).__new__(cls)

print "Medium2 new"

class Leaf(Medium1, Medium2):

def __new__(cls):

obj = super(Leaf, cls).__new__(cls)

print "Leaf new"

return obj

super(Leaf, cls).__new__(cls)的意思是说:获取cls这个类的mro,这里也是[Leaf, Medium1, Medium2, Base]

从mro中Leaf右边的一个类开始,依次寻找__new__函数

一旦找到,就返回“非绑定”的__new__函数

由于返回的是非绑定的函数对象,因此调用时不能省略函数的第一个参数。这也是这里调用__new__时,需要传入参数cls的原因

同样的,如果我们想从某个mro的某个位置开始查找,只需要修改super的第一个参数就行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值