Python - 多继承中的super()与MRO

Python是完全的面向对象的语言,因此它具有面向对象语言的特点: 封装,继承,多态。

在学习PyTorch定义模块时遇到了这个问题,趁此机会把Python多继承的细节搞清楚。

继承:

单继承:

#父类
class Animal(object):
	"""docstring for Animal"""
	def __init__(self, name):
		self.name = name;

	def run(self):
		print("[{0}] is running".format(self.name))

	def eat(self):
		print("[{0}] is eatting".format(self.name))
		
#子类
class Dog(Animal):
	def __init__(self, name):
		self.name = name;
	# 子类调用父类中已有的方法
	def animal_run(self):
		print("\nCalling father's method:")
		Animal.run(self)


dog = Dog("Chichi")
dog.run()
dog.eat()
dog.animal_run()

运行结果:

[Chichi] is running
[Chichi] is eatting

Calling father's method:
[Chichi] is running

在单继承的情况下,我们可以直接在子类中通过 父类名.Method() 来调用父类的方法。

多继承:

class A(object):
    def __init__(self):
        self.n = 'A'
        print("I'm A, I am called from C")
    def func(self):
        print("self is {0} in A.func".format(self))
        self.n += 'A'


class B(A):
    def __init__(self):
        self.n = 'B'
        print("I'm B, I am called from D")
    def func(self):
        print("self is {0} in B.func".format(self))
        super().func()
        self.n += 'B'


class C(A):
    def __init__(self):
        self.n = 'C'
        print("I'm C, I am called from B")

    def func(self):
        print("self is {0} in C.func".format(self))
        super().func()
        self.n += 'C'


class D(B, C):
    def __init__(self):
        self.n = 'D'
        super(D, self).func()

    def func(self):
        print("self is {0} in D.func".format(self))
        # super().func()
        print(D.mro())
        A.__init__(self)
        print("Calling by super():")
        super(B,self).__init__()
        self.n += 'D'

d = D()
d.func()

运行结果:

self is <__main__.D object at 0x10ff3d7b8> in B.func
self is <__main__.D object at 0x10ff3d7b8> in C.func
self is <__main__.D object at 0x10ff3d7b8> in A.func
self is <__main__.D object at 0x10ff3d7b8> in D.func
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
I'm A, I am called from C
Calling by super():
I'm C, I am called from B

在多继承中,我们也可以直接指明父类名并调用其中的方法,但这样做需要我们直接写出父类名,这在很多时候是很麻烦的事情。当该子类的父类名称变化时,我们就需要修改很多地方的调用,所以在Python2中引入了super()方法。

1. 我们在写D的__init__()方法时,可以直接用如下代码来调用其父类的初始化方法,然后在父类的基础上继续添加子类的个性化方法。

super(D,self).__init__()

2. 甚至我们可以完全不写D的__init__(),当创建D的对象时,自动从父类继承__init()__方法。

但在多继承中,

1. 一个子类有两个父类时,应该调用哪个父类的方法呢?

2. 该例中,super是怎么决定调用B还是C的init的呢?

3. 或者当直接的父类也没有init函数,那是不是就直接继续去找父类的父类呢?

Python多继承问题实质上是钻石继承问题(Diamond Inheritance):


对于该问题的解决方法是,规定一个MRO(Method Resolution Order),所以在上例中,MRO是D->B->C->A->Object, 按该顺序调用我们就能找到上面问题的答案。

1. 多继承中,子类会按照MRO调用排在自己下一个的类中的方法。

2. 按照MRO中的顺序调用。

3. 当最初被调用的类没有该方法时,Python会按照MRO的顺序一个一个向前查找,直到找到该方法。但需要注意的是,当D,B都没有定义init时,调用的是C 而不是A 的init。

那么MRO顺序是怎么确定的呢?

一句话总结:父类总是出现在子类后,若有多个父类,其相对顺序保持不变,在定义子类调参时,父类的顺序就已经决定了MRO顺序。

这里有一篇对MRO顺序的探究,给出了很多有趣的例子。



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值