python在多继承中子类调用某个父类的同名方法之 super讲解(mro机制)***

目录

2.   钻石继承遇到的难题

3.   针对钻石问题各语言的解决方法

3.1. C++

3.2. Java

3.3. Ruby

3.4. Python

4.   super的内核:mro

5.   super的具体用法

5.1. super(type, obj)

super(Leaf, self).__init__()的意思是说:

5.2. super(type, type2)

super(Leaf, cls).__new__(cls)的意思是说:

6.   小结

方法一:调用未绑定的超类构造方法

方法二:使用super函数

方法一:把对象调用转换为类调用:A.f_a(self)、B.f_a(self)

方法二:使用super()机制,引入super()方法:super(C,self).f_a()、super(A,self).f_a()


python中的super( test, self).__init__()

python中的super( test, self).__init__()

 对继承自父类的属性进行初始化

首先找到test的父类(比如是类A),然后把类test的对象self转换为类A的对象,然后“被转换”的类A对象调用自己的__init__函数

python子类调用父类成员有2种方法,分别是普通方法和super方法

 

###################################################################################################

print("#####################基类中访问子类的成员(C++中this不允许,除非做强制类型向下转换)############################")
class A(object):
	def __init__(self):
		print("A::__init__\n")

	def fn(self):
		print("A::fn x={}".format(self.x))		##父类中可以访问子类中的成员变量
		#print("A::fn y={}".format(self.y))		##在访问变量之前,变量一定要出现过定义才可以,否则:AttributeError: 'B' object has no attribute 'y'


class B(A):
	def __init__(self):
		self.x = 1
		print(self.__class__)
		##调用基类函数
		print("super(B, self).__init__()")
		super(B, self).__init__()

		# ###TypeError: super() argument 1 must be type, not B
		# print("super(self, self).__init__()")
		# super(self, self).__init__()

		print("super(self.__class__, self).__init__()")
		super(self.__class__, self).__init__()

		print("su.__init__()")
		su = super(self.__class__, self)
		print(type(su))
		su.__init__()

b = B()
b.fn()

##在基类中对象中差找不到x成员,执行也会报错
# a = A()
# a.fn()

 

python在多继承中子类调用某个父类的同名方法之 super讲解(mro机制)***_第1张图片

###################################################################################################

假设Base是基类

  1. class Base(object):
    
        def __init__(self):
    
            print “Base init”

     

  2. 则普通方法如下
  3. class Leaf(Base):
    
        def __init__(self):
    
            Base.__init__(self)
    
            print “Leaf init”

     

  4. super方法如下
  5. class Leaf(Base):
    
        def __init__(self):
    
            super(Leaf, self).__init__()
    
            print “Leaf init”

     

  6. 在上面的简单场景下,两种方法的效果一致:

>>> leaf = Leaf()

Base init

Leaf init

2.   钻石继承遇到的难题

当我们来到钻石继承场景时,我们就遇到了一个难题:

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

我们把第2章的钻石继承用super重写一下,看一下输出结果

class Base(object):

    def __init__(self):

        print “Base init”


class Medium1(Base):

    def __init__(self):

        super(Medium1, self).__init__()

        print “Medium1 init”


class Medium2(Base):

    def __init__(self):

        super(Medium2, self).__init__()

        print “Medium2 init”


class Leaf(Medium1, Medium2):

    def __init__(self):

        super(Leaf, self).__init__()

        print “Leaf init”

我们生成Leaf对象:

>>> leaf = Leaf()

Base init

Medium2 init

Medium1 init

Leaf init

可以看到整个初始化过程符合我们的预期,Base只被初始化了1次。而且重要的是,相比原来的普通写法,super方法并没有写额外的代码,也没有引入额外的概念

4.   super的内核:mro

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

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

>>> Leaf.mro()

[Leaf, Medium1, Medium2, Base]

可以看到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:https://en.wikipedia.org/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)

光从字面来看,这可以算是python中最语焉不详的帮助文档之一了。甚至里面还有一些术语误用。那super究竟应该怎么用呢,我们重点来看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__()的意思是说:

  1. 获取self所属类的mro, 也就是[Leaf, Medium1, Medium2, Base]
  2. 从mro中Leaf右边的一个类开始,依次寻找__init__函数。这里是从Medium1开始寻找
  3. 一旦找到,就把找到的__init__函数绑定到self对象,并返回

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

5.2. super(type, type2)

当我们在Leaf中写类方法的super时:

 
  1. class Leaf(Medium1, Medium2):

  2. def __new__(cls):

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

  4. print “Leaf new”

  5. return obj

super(Leaf, cls).__new__(cls)的意思是说:

  1. 获取cls这个类的mro,这里也是[Leaf, Medium1, Medium2, Base]
  2. 从mro中Leaf右边的一个类开始,依次寻找__new__函数
  3. 一旦找到,就返回“ 非绑定 ”的__new__函数

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

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

6.   小结

至此,我们讲解了和super相关的用法及原理,小结一下我们讲过的内容有:

  1. python调用父类成员共有2种方法:普通方法,super方法
  2. 在钻石继承中,普通方法会遇到Base类两次初始化的问题
  3. 简述了其他语言对这个问题的解决方法,并用实例展示了python使用super可以解决此问题
  4. 在讲super具体用法前,先讲了super的内核:mro的知识和原理
  5. 讲解了super两种主要的用法及原理

super 的用法和原理

一、前言

Python 面向对象中有继承这个概念,初学时感觉很牛逼,里面也有个super类,经常见到,最近做一些题才算是理解了。特地记录分享给后来研究的小伙伴,毕竟现在小学生都开始学了(滑稽脸)

二、代码

直接上干货,能把下面一个问题全答对,后面就不用看了。

class A():
    def go(self):
        print ("go A go!")
    def stop(self):
        print ("stop A stop!")
    def pause(self):
        raise Exception("Not Implemented")
class B(A):
    def go(self):
        super(B, self).go()
        print ("go B go!")
class C(A):
    def go(self):
        super(C, self).go()
        print ("go C go!")
    def stop(self):
        super(C, self).stop()
        print ("stop C stop!")
class D(B,C):
    def go(self):
        super(D, self).go()
        print ("go D go!")
    def stop(self):
        super(D, self).stop()
        print ("stop D stop!")
    def pause(self):
        print ("wait D wait!")
class E(B,C):
    pass
a = A()
b = B()
c = C()
d = D()
e = E()
# 说明下列代码的输出结果
a.go()
print('--------')
b.go()
print('--------')
c.go()
print('--------')
d.go()
print('--------')
e.go()
print('--------')
a.stop()
print('--------')
b.stop()
print('--------')
c.stop()
print('--------')
d.stop()
print('--------')
e.stop()
print(D.mro())
a.pause()
b.pause()
c.pause()
d.pause()
e.pause()

当然,直接运行就有答案了,还是要仔细想一下,反正看到我第一次跑出的结果的时候,我都不敢相信自己的眼睛。

step1:

几个概念:

继承的功能:父类的代码重用

多态的功能:同一方法对不同类型的对象会有相应的结果

开闭原则:对扩展开放,对修改封闭

super类功能:新式类实现广度优先的不重复的调用父类,解决了钻石继承(多继承)的难题

 

step2:

super实现原理:通过c3算法生成mro(method resolution order)列表,根据列表中元素顺序查询调用

新式类调用顺序为广度优先,旧式类为深度优先,可以通过__mro__来查看

 

step3:

个人理解:

1.调用了父类的方法,出入的是子类的实例对象

2.新式类子类(A,B),A就在B之前

3.super类似于嵌套的一种设计,当代码执行到super实例化后,先去找同级父类,若没有其余父类,再执行自身父类,再往下走,

  简洁点的三个原则就是:

子类在父类前,所有类不重复调用,从左到右

 

理解了以上的说法,题目就没问题了。

也不用跑了,答案如下:

a.go()# go A go!
b.go()# go A go!# go B go!
c.go()# go A go!# go C go!
d.go()# go A go!# go C go!# go B go!# go D go!
e.go()# go A go!# go C go!# go B go!
a.stop()# stop A stop!
b.stop()# stop A stop!
c.stop()# stop A stop!# stop C stop!
d.stop()# stop A stop!# stop C stop!# stop D stop!
e.stop()# stop A stop!
a.pause()# ... Exception: Not Implemented
b.pause()# ... Exception: Not Implemented
c.pause()# ... Exception: Not Implemented
d.pause()# wait D wait!
e.pause()# ...Exception: Not Implemented

 

看了答案,其实还有一点,父类抛异常的情况,如果子类有不抛异常的方法,异常就不抛出了,这个设计也会很有用。

这里就中间一个A,C,B,D的和网上常见的不太一样,促使我仔细研究了一下,其实就是个人理解第三条。

补充:

Python2 和Python3在这个问题上的差别

 

Python2 没有默认继承object

Python3 默认全部继承object类,都是新式类

 

Python2super调用 super(开始类名,self).函数名()

Python3  super().函数名()

 

关于调用父类函数传入子类实例的栗子举一个:

class A:
    def __init__(self):
        self.n = 2

    def add(self, m):
        print('self is {0} @A.add'.format(self))
        self.n += m


class B(A):
    def __init__(self):
        self.n = 3

    def add(self, m):
        print('self is {0} @B.add'.format(self))
        super().add(m)
        print('newb')
        self.n += 3


class C(A):
    def __init__(self):
        self.n = 4

    def add(self, m):
        print('self is {0} @C.add'.format(self))
        super().add(m)
        print('newc')
        self.n += 4


class D(B, C):
    def __init__(self):
        self.n = 5

    def add(self, m):
        print('self is {0} @D.add'.format(self))
        super().add(m)
        self.n += 5


d = D()
d.add(2)
print(d.n)

 


# -*- encoding:utf-8 -*-

class myBase1(object):
	"""docstring for myBase1"""
	def __init__(self):
		super(myBase1, self).__init__()
		print("myBase1::__init__")

	def function(self):
		self.function_child()			#在父类中调用子类的方法,在C++中不可以
		print(self.name)				#在父类中调用子类的数据成员,在C++中不可以
		print("myBase1::function")

class myBase2(object):
	"""docstring for myBase2"""
	def __init__(self):
		super(myBase2, self).__init__()
		print("myBase2::__init__")

	def function(self):
		self.function_child()			#在父类中调用子类的方法,在C++中不可以
		print(self.name)				#在父类中调用子类的数据成员,在C++中不可以
		print("myBase2::function")

class myBase3(object):
	"""docstring for myBase3"""
	def __init__(self):
		super(myBase3, self).__init__()
		print("myBase3::__init__")

	def function(self):
		self.function_child()			#在父类中调用子类的方法,在C++中不可以
		print(self.name)				#在父类中调用子类的数据成员,在C++中不可以
		print("myBase3::function")


class myBase4(object):
	"""docstring for myBase4"""
	def __init__(self):
		#super(myBase4, self).__init__()			#注意这里屏蔽掉了super函数
		print("myBase4::__init__")


class myBase5(object):
	"""docstring for myBase5"""
	def __init__(self):
		#super(myBase5, self).__init__()			#注意这里屏蔽掉了super函数
		print("myBase5::__init__")

class myChild(myBase1, myBase2, myBase3, myBase4, myBase5):
	"""docstring for myChild"""
	def __init__(self, name):
		#myBase.__init__(self)						#调用父类的方法:直接法 形如:parentclass.parentattribute(self,[arg]),使用父类名称直接调用.基类的调用顺序,按着基类的继承顺序从左到右,A、B、C、D。先A-->D.如果直接写B.__init__(self)则基类A的构造函数不会被执行
													#会被过滤掉,B、C、D构造函数会被执行。
		myBase3.__init__(self)
		print("******myBase3.__init__******myBase3类中使用了super继续调用右边基类的构造函数,myBase4、myBase5中没有使用super函数,执行到myBase4中截止了。不再搜索右边的基类\n")
		"""
		输出:
			myBase4::__init__
			myBase3::__init__
			******myBase3.__init__******myBase3类中使用了super继续调用右边基类的构造函数,myBase4、myBase5中没有使用super函数,执行到myBase4中截止了。不再搜索右边的基类
		结论:
			myBase4类中没有使用了super,则不会继续往右边执行。myBase5中没有使用super函数
		"""

		myBase4.__init__(self)
		print("******myBase4.__init__******myBase4类中没有使用了super,myBase5中没有使用super函数\n")
		"""
		输出:
			myBase4::__init__
			******myBase4.__init__******myBase4类中没有使用了super,myBase5中没有使用super函数
		结论:
			执行到myBase4中截止了。不再搜索右边的基类
		"""

		super(myChild, self).__init__()				#调用父类的方法:super函数,形如:super(childclass, childobj).parentattribute([arg])
		print("myChild(myBase1, myBase2, myBase3)==>super(myChild, self).__init__\n")
		"""
		输出:
			myBase3::__init__
			myBase2::__init__
			myBase1::__init__
			myChild(myBase1, myBase2, myBase3) == > super(myChild, self).__init__
		结论:
        	执行[myBase1, myBase2, myBase3]从最右边基类开始执行构造: 执行顺序:myBase3==>myBase2==>myBase1
		"""
		super(myBase1, self).__init__()  			#调用myBase1后面的基类myBase2、myBase3的构造函数。myBase1被过滤掉
		print("myChild(myBase1, myBase2, myBase3)==>super(myBase1, self).__init__\n")
		"""
		输出:
			myBase3::__init__
			myBase2::__init__
			myChild(myBase1, myBase2, myBase3)==>super(myBase1, self).__init__
		结论:
        	执行[myBase2, myBase3]从最右边基类开始执行构造,执行顺序:myBase3==>myBase2
		"""

		super(myBase2, self).__init__()
		print("myChild(myBase1, myBase2, myBase3)==>super(myBase2, self).__init__\n")
		"""
		输出:
			myBase3::__init__
			myChild(myBase1, myBase2, myBase3)==>super(myBase2, self).__init__
		结论:
        	执行[myBase3]从最右边基类开始执行构造,执行顺序:myBase3
		"""

		super(myBase3, self).__init__()
		print("myChild(myBase1, myBase2, myBase3)==>super(myBase3, self).__init__\n")
		"""
		输出:
        	myChild(myBase1, myBase2, myBase3)==>super(myBase2, self).__init__
        结论:
        	super(myBase3, self).__init__()由于myBase3右边没有可以调用的基类的构造函数所以直接调用object基类的构造函数了。此处没有打印
        """

		super().__init__()							#调用父类的方法 super函数省略法,形如:super().parentattribute([arg])
		print("myChild(myBase1, myBase2, myBase3)==>super().__init__******super函数省略法******\n")
		"""
		输出:
			myBase3::__init__
			myBase2::__init__
			myBase1::__init__
			myChild(myBase1, myBase2, myBase3)==>super().__init__
		结论:
			super().__init__() 缺省写法等价于 super(myChild, self).__init__(),所以输出和第一种一样
		"""

		#super(myBase2, self).__init__()  # 调用myBase3后面的构造函数,myBase1和myBase2都被过滤掉了
		myBase1.__init__(self)
		print("myChild(myBase1, myBase2, myBase3)==>super(myBase2, self).__init__\n")
		"""
		输出:
			myBase3::__init__
			myBase3::__init__
			myBase2::__init__
			myChild(myBase1, myBase2, myBase3)==>super(myBase2, self).__init__
		结论:
			第一行super(myBase2, self).__init__()调用了myBase2右边继承的基类(myBase2, myBase3],所以输出myBase3::__init__
			第二行myBase2.__init__(self)执行[myBase2, myBase3]从最右边基类开始执行构造,所以输出myBase3::__init__、myBase2::__init__
        """

		self.name = name
		print("myChild::__init__\n")

	def function_child(self):
		print("myChild::function_child")

class myChild2(myBase1):
	"""docstring for myChild2"""
	def __init__(self, name):
		myBase1.__init__(self)						#调用父类的方法:直接法 形如:parentclass.parentattribute(self,[arg]),使用父类名称直接调用
		#super(myChild, self).__init__()			#调用父类的方法:super函数,形如:super(childclass, childobj).parentattribute([arg])
		#super().__init__()							#调用父类的方法 super函数省略法,形如:super().parentattribute([arg])
		print("myChild2::__init__")
		self.name = name

	def function_child(self):
		print("myChild2::function_child")

mych = myChild("child")
#mych.function()

#mybase = myBase()
#mybase.function()						#报错,提示在myBase 对象中没有name属性

python和其他面向对象语言类似,每个类可以拥有一个或者多个父类,它们从父类那里继承了属性和方法。如果一个方法在子类的实例中被调用,或者一个属性在子类的实例中被访问,但是该方法或属性在子类中并不存在,那么就会自动的去其父类中进行查找。

继承父类后,就能调用父类方法和访问父类属性,而要完成整个集成过程,子类是需要调用的构造函数的。

子类不显式调用父类的构造方法,而父类构造函数初始化了一些属性,就会出现问题

如果子类和父类都有构造函数,子类其实是重写了父类的构造函数,如果不显式调用父类构造函数,父类的构造函数就不会被执行,导致子类实例访问父类初始化方法中初始的变量就会出现问题。

class A:   #注意此处没有继承新式python类object
    def __init__(self):
        self.namea="aaa"
  
    def funca(self):
        print("function a : %s"%self.namea)
  
class B(A):
    def __init__(self):
        self.nameb="bbb"
  
    def funcb(self):
        print("function b : %s"%self.nameb)
  
b=B()
print(b.nameb)
b.funcb()
  
b.funca()


 结果:

bbb
function b : bbb
Traceback (most recent call last):
  File "/home/lai/workspace/sru/2.py", line 24, in 
    b.funca()
  File "/home/lai/workspace/sru/2.py", line 11, in funca
    print("function a : %s"%self.namea)
AttributeError: 'B' object has no attribute 'namea'


方法一:调用未绑定的超类构造方法

在子类中,构造函数被重写,但新的构造方法没有任何关于初始化父类的namea属性的代码,为了达到预期的效果,子类的构造方法必须调用其父类的构造方法来进行基本的初始化。有两种方法能达到这个目的:调用超类构造方法的未绑定版本,或者使用super函数

修改代码,多增一行:

class A:  #注意此处没有继承新式python类object
    def __init__(self):
        self.namea="aaa"
 
    def funca(self):
        print("function a : %s"%self.namea)
 
class B(A):
    def __init__(self):
        self.nameb="bbb"        
        A.__init__(self)    #这一行解决了问题

    def funcb(self):
        print("function b : %s"%self.nameb)
 
b=B()
print(b.nameb)
b.funcb()
 
b.funca()

 结果

 

bbb
function b : bbb
function a : aaa

如上有注释的一行解决了该问题,直接使用父类名称调用其构造函数即可。

***这种方法叫做调用父类的未绑定的构造方法。在调用一个实例的方法时,该方法的self参数会被自动绑定到实例上(称为绑定方法)。但如果直接调用类的方法(比如A.__init),那么就没有实例会被绑定。这样就可以自由的提供需要的self参数,这种方法称为未绑定unbound方法

通过将当前的实例作为self参数提供给未绑定方法,B类就能使用其父类构造方法的所有实现,从而namea变量被设置。

方法二:使用super函数

修改代码,这次需要增加在原来代码上增加2行:


class A(object): #父类需要继承object对象,新式python类
    def __init__(self):
        self.namea="aaa"
  
    def funca(self):
        print("function a : %s"%self.namea)
  
class B(A):
    def __init__(self):        
        super(B,self).__init__() #这一行解决问题
        self.nameb="bbb"
  
    def funcb(self):
        print("function b : %s"%self.nameb)
  
b=B()
print(b.nameb)
b.funcb()

 

bbb
function b : bbb
function a : aaa

 

 结果与方法一相同

如上有注释的为新增的代码,其中第一句让类A继承自object类,这样才能使用super函数,因为这是python的“新式类”支持的特性。当前的类和对象可以作为super函数的参数使用,调用函数返回的对象的任何方法都是调用超类的方法,而不是当前类的方法。

super函数会返回一个super对象,这个对象负责进行方法解析,解析过程其会自动查找所有的父类以及父类的父类

 方法一更直观,方法二可以一次初始化所有超类

super函数比在超类中直接调用未绑定方法更直观,但是其最大的优点是如果子类继承了多个父类,它只需要使用一次super函数就可以。然而如果没有这个需求,直接使用A.__init__(self)更直观一些。

 

pycharm简单敲了一个多继承的代码,为了方便大家看懂,这个代码真心简单:

class A:
    def f_a(self):
        print('--------A--------')
class B:
    def f_a(self):
        print('-------B-------')
class C(A,B):
    def f_a(self):
        print('--------C--------')
c = C()
c.f_a()
》》》--------C--------


看,如果我们想在C类中的f_a方法里面使用父类A或者父类B的方法该如何操作呢
方法有两种:

方法一:把对象调用转换为类调用:A.f_a(self)、B.f_a(self)

class C(A,B):
    def f_a(self):
        A.f_a(self)
        B.f_a(self)
        print('--------C--------')
c = C()
c.f_a()
》》》--------A--------
》》》-------B-------
》》》--------C--------


这里调用父类的f_a方法时括号里面要写self,表明这是一个类调用,但这种方法有一个缺点,比如说如果修改了父类的名称,那么在子类中会涉及多出修改,并且python是允许多继承的语言,上述方法在多继承时就要重复写多次,显得累赘,为了解决这些问题,python引进了super()机制,接下来想必大家都猜到了下一种调用父类的另一种方法了吧。


方法二:使用super()机制,引入super()方法:super(C,self).f_a()、super(A,self).f_a()

class C(A,B):
    def f_a(self):
        super().f_a()
        print('--------C--------')
c = C()
c.f_a()
》》》--------A--------
》》》--------C--------


这里直接使用super()方法会调用A类的f_a方法,因为它会默认多继承中从左到右的顺序来调用,那么有人就会问了,假如我想调用B类中的f_a方法是不是把A和B对调下呢,这种方法也行,不过这不算的上是一种较为巧妙的方法。
我们还可以使用super()方法,但要对其修改下:

class C(A,B):
    def f_a(self):
        super(C,self).f_a()
        super(A,self).f_a()
        print('--------C--------')
c = C()
c.f_a()
》》》--------A--------
》》》-------B-------
》》》--------C--------


看到没,这样写:super(C,self).f_a()会调用最左边的(即A类)的f_a方法,而super(A,self).f_a()会调用A类后面那个类的f_a()方法,若果继承的不止两个类,如果要调用某个类方法,只要知道前一个类名就可以调用。

你可能感兴趣的:(python基础)