多继承的实现

class A(object):

    def out(self):
        print("A类方法")

class B(object):

    def out(self):
        print("B类方法")

class C(A, B):
    pass

c = C()
# 打印C类的调用路径顺序(注意要类名.__mro__)
print(C.__mro__)
c.out()

运行结果:

(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
A类方法

可以尝试一下把 C类 的继承顺序改成 B,A

class C(B, A):
    pass

结果就是

(<class ‘main.C’>, <class ‘main.B’>, <class ‘object’>, <class ‘main.A’>)
B类方法

如果 C类out() 方法重写那么将执行 C类的 out() 方法

class C(B, A):
    
    def out(self):
        print("C类方法")

结果如下:

(<class ‘main.C’>, <class ‘main.B’>, <class ‘object’>, <class ‘main.A’>)
C类方法

了解MRO

新式类可以直接通过 类名.__mro__ 的方式获取类的 MRO,也可以通过 类名.mro() 的形式,旧式类是没有 mro 属性和 mro() 方法的。

方法解析顺序 Method Resolution Order,简称 MRO。主要用于在多继承时判断方法,属性的调用路径。

  • 在搜索方法时,是按照 mro() 输出的结果,从左到右的顺序查找的
  • 如果找到,在当前类中找到方法就直接执行,不在搜索
  • 没有找到,就依次查找下一个类中是否有对应的方法,找到执行,不在搜索
  • 如果最后一个类,还没有找到方法,程序报错

MRO 的顺序是根据 Python中C3算法 得来的大家感兴趣可以去研究一下,这里就不在赘述了。

新式类和旧式类

在早期版本的 Python 中,所有类并没有一个共同的祖先 object,如果定义一个类,但没有显式指定其祖先,那么就被解释为 旧式类,例如:

class oldA:  
	pass

class oldB:
    pass

其中,oldAoldB 都属于旧式类

Python 2.x 版本中,为了向后兼容保留了旧式类。该版本中的 新式类必须 显式继承 object 或者其他新式类:

class NewA(object):  
    pass

class NewB(NewA):  
    pass

显然,以上两个类都属于 新式类

而在 Python 3.x 版本中,不再保留旧式类的概念。因此,没有继承任何其他类的类都隐式地继承自 object

super()的使用

super() 函数是用于调用父类(超类)的一个方法。

super() 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。

单继承

父类名调用
"""
单继承使用父类名调用
"""

class Parent(object):

	def eat(self):
		print("\tparent --- 爱吃饭")


class Son1(Parent):


	def eat(self):
		print("son1 --- eat()")
		Parent.eat(self)
		print("\tson1  ---  爱吃蔬菜\n")
	

class Son2(Parent):


	def eat(self):
		print("son2 --- eat()")
		Parent.eat(self)
		print("\tson2  ---  爱吃水果\n")


def main():
	s1 = Son1()
	s2 = Son2()

	s1.eat()
	s2.eat()

    
if __name__ == '__main__':
	main()

运行结果

son1 --- eat()
	parent --- 爱吃饭
	son1  ---  爱吃蔬菜

son2 --- eat()
	parent --- 爱吃饭
	son2  ---  爱吃水果

使用super()
"""
单继承中super()的使用
"""

class Parent(object):

	def eat(self):
		print("\tparent --- 爱吃饭")


class Son1(Parent):


	def eat(self):
		print("son1 --- eat()")
		super().eat()
		print("\tson1  ---  爱吃蔬菜\n")
	

class Son2(Parent):


	def eat(self):
		print("son2 --- eat()")
		super().eat()
		print("\tson2  ---  爱吃水果\n")


def main():
	s1 = Son1()
	s2 = Son2()

	s1.eat()
	s2.eat()


if __name__ == '__main__':
	main()

运行结果:

son1 --- eat()
	parent --- 爱吃饭
	son1  ---  爱吃蔬菜

son2 --- eat()
	parent --- 爱吃饭
	son2  ---  爱吃水果

可以发现在单继承使用 父类名super() 调用父类方法结果都一样。

多继承

还是上面例子,就是加一个 GrandSon 类,让它继承 Son1, Son2 。让 eat() 具备其父类的特性。

父类名调用
"""
多继承中父类名的使用
"""

class Parent(object):

	def eat(self):
		print("\tparent --- 爱吃饭")


class Son1(Parent):


	def eat(self):
		print("son1 --- eat()")
		Parent.eat(self)
		print("\tson1  ---  爱吃蔬菜\n")
	

class Son2(Parent):


	def eat(self):
		print("son2 --- eat()")
		Parent.eat(self)
		print("\tson2  ---  爱吃水果\n")


class Grandson(Son1, Son2):
	
	def eat(self):
		print("grandson --- eat()")
		# super().eat()
		Son1.eat(self)
		Son2.eat(self)
		print("\tgrandson --- 爱吃零食")


def main():
	# s1 = Son1()
	# s2 = Son2()

	# s1.eat()
	# s2.eat()

	g = Grandson()
	g.eat()


if __name__ == '__main__':
	main()

运行结果

grandson --- eat()
son1 --- eat()
	parent --- 爱吃饭
	son1  ---  爱吃蔬菜

son2 --- eat()
	parent --- 爱吃饭
	son2  ---  爱吃水果

	grandson --- 爱吃零食

结果显示 Parenteat() 方法调用了多次,存在重复调用。

使用super()
"""
多继承中super()的使用
"""

class Parent(object):

	def eat(self):
		print("\tparent --- 爱吃饭")


class Son1(Parent):


	def eat(self):
		print("son1 --- eat()")
		super().eat()
		print("\tson1  ---  爱吃蔬菜\n")
	

class Son2(Parent):


	def eat(self):
		print("son2 --- eat()")
		super().eat()
		print("\tson2  ---  爱吃水果\n")


class Grandson(Son1, Son2):
	
	def eat(self):
		print("grandson --- eat()")
		super().eat()
		print("\tgrandson --- 爱吃零食")


def main():
	g = Grandson()
	g.eat()


if __name__ == '__main__':
	main()

运行结果

grandson --- eat()
son1 --- eat()
son2 --- eat()
	parent --- 爱吃饭
	son2  ---  爱吃水果

	son1  ---  爱吃蔬菜

	grandson --- 爱吃零食

可以发现在多继承中使用 super() 没有重复调用。

假如在多继承中 Grandson 类的 eat() 方法只想复用 Parent, Son1eat()的方法,不需要 Son2的。该如何实现呢?

父类名调用

"""
父类名的使用
"""

class Parent(object):

	def eat(self):
		print("\tparent --- 爱吃饭")


class Son1(Parent):

	def eat(self):
		print("son1 --- eat()")
		Parent.eat(self)
		print("\tson1  ---  爱吃蔬菜\n")
	

class Son2(Parent):

	def eat(self):
		print("son2 --- eat()")
		Parent.eat(self)
		print("\tson2  ---  爱吃水果\n")


class Grandson(Son1, Son2):
	
	def eat(self):
		print("grandson --- eat()")
		Son1.eat(self)
		# Son2.eat(self)
		print("\tgrandson --- 爱吃零食")


def main():
	print(Grandson.mro())
	g = Grandson()
	g.eat()


if __name__ == '__main__':
	main()

结果如下

[<class '__main__.Grandson'>, 
 <class '__main__.Son1'>, 
 <class '__main__.Son2'>, 
 <class '__main__.Parent'>, 
 <class 'object'>
]

grandson --- eat()
son1 --- eat()
	parent --- 爱吃饭
	son1  ---  爱吃蔬菜

	grandson --- 爱吃零食
[Finished in 0.1s]

super()调用

"""
super()的使用
"""

class Parent(object):

	def eat(self):
		print("\tparent --- 爱吃饭")


class Son1(Parent):

	def eat(self):
		print("son1 --- eat()")
		super().eat()
		print("\tson1  ---  爱吃蔬菜\n")
	

class Son2(Parent):

	def eat(self):
		print("son2 --- eat()")
		super().eat()
		print("\tson2  ---  爱吃水果\n")


class Grandson(Son1, Son2):
	
	def eat(self):
		print("grandson --- eat()")
		super(Son1, self).eat()
		# Son1.eat(self)
		# Son2.eat(self)
		print("\tgrandson --- 爱吃零食")


def main():
	print(Grandson.mro())
	g = Grandson()
	g.eat()


if __name__ == '__main__':
	main()

结果如下

[<class '__main__.Grandson'>, 
 <class '__main__.Son1'>, 
 <class '__main__.Son2'>, 
 <class '__main__.Parent'>, 
 <class 'object'>
]

grandson --- eat()
son2 --- eat()
	parent --- 爱吃饭
	son2  ---  爱吃水果

	grandson --- 爱吃零食
[Finished in 0.1s]

然而却发现 super(Son1, self).eat() 调用的是 Son2eat() 方法。

是因为 MRO 的原因,当调用 super(Son1,self).eat() 时 ,会拿 Son1GrandsonMRO方法解析顺序表 中寻找,找到然后 super() 调用则是列表中下一个,这里是 Son2,然后 Son2.eat() 中使用了 super().eat(),此时是拿其本身 Son2Son2MRO方法解析顺序表 中寻找,然后 super().eat() 调用,则是列表的下一个 Parent.eat()

因此在 Grandsonsuper(Son1, self).eat() 调用的是 Son2.eat()

假如 super(Son2, self).eat() 调用的则是 Parent.eat()。结果如下

[<class '__main__.Grandson'>, 
 <class '__main__.Son1'>, 
 <class '__main__.Son2'>, 
 <class '__main__.Parent'>, 
 <class 'object'>
]
grandson --- eat()
	parent --- 爱吃饭
	grandson --- 爱吃零食

因此只能使用 父类名 的形式调用 Son1.eat()只继承 Son1 的特性。如果不清楚 super()调用的谁,打印其

类名.mro(),对照 MRO方法解析顺序表,就一目了然。

总结

  • 方法解析顺序 Method Resolution Order,简称 MRO。主要用于在多继承时判断方法,属性的调用路径
  • 旧式类,没有共同的 object祖先且没有显式的指定其祖先。
  • 新式类,在 Python 2.x 版本中显式继承 object 或者其他新式类,Python3.x中则是隐式继承object
  • super().method() 相对于 类名.method(self),在 单继承 上用法基本无差
  • 多继承 上有区别,super() 方法 能保证每个父类的方法只会执行一次,而使用类名的方法会导致方法被执行多次,具体看前面的输出结果