(小甲鱼python)类和对象(III)总结 类的继承---构造函数、重写、钻石继承、super()函数

一、基础复习

1.类和对象(I)总结 类的定义、self的用法
2.类的继承(II)、类的判断isinstance()、issubclass()、多重继承、类的组合
3.类和对象(III)总结 类的绑定,self,__dict__的妙用

二、类的继承

1.构造函数
这个函数是可以通过参数来进行个性化定制的。只要在定义类的同时定义一个构造函数,就可以自由发挥了。
构造函数有一个特殊的名称叫__init__(),,只需要在类中定义__init__方法,就可以在实例化对象的同时实现个性化定制。
例1:

>>> class C:
	def __init__(self,x,y):
		self.x=x   #等号左边的x是绑定到实例化对象里面的x属性,等号右边的x是传进来的参数。
		self.y=y
	def add(self):
		return self.x+self.y
	def mul(self):
		return self.x*self.y

>>> 
>>> c=C(2,3)
>>> c.add()
5
>>> c.mul()
6
>>> c.__dict__
{'x': 2, 'y': 3}
>>> d=C(4,5)
>>> d.__dict__
{'x': 4, 'y': 5}
>>> d.add()
9
>>> d.mul()
20
>>> 

2.重写
如果对于父类的某个属性或某个方法不满意的话,完全可以重新写一个同名的属性或方法对其进行覆盖。
例2:

>>> class D(C):
	def __init__(self,x,y,z):
		C.__init__(self,x,y)
		self.z=z
	def add(self):
		return C.add(self)+self.z
	def mul(self):
		return C.mul(self)*self.z

	
>>> d=D(2,3,4)
>>> d.add()
9
>>> d.mul()
24
>>> 

3.钻石继承
在这里插入图片描述

具体看例3
例3:

>>> class A:
	def __init__(self):
		print("哈喽,我是A")

>>> class B1(A):
	def __init__(self):
		A.__init__(self)
		print("哈喽,我是B1")

>>> class B2(A):
	def __init__(self):
		A.__init__(self)
		print("哈喽,我是B2")

		
>>> class C(B1,B2):
	def __init__(self):
		B1.__init__(self)
		B2.__init__(self)
		print("哈喽,我是C")

		
>>> c=C()
哈喽,我是A              # A重复初始化了两次,因为C继承自B1,B2,而B1,B2继承自A,因此A重复了两次
哈喽,我是B1
哈喽,我是A
哈喽,我是B2
哈喽,我是C

为了解决钻石继承的问题,引入了super()函数。
4.super()函数

super()函数能够在父类中搜索指定的方法,并自动绑定好self参数。
使用super()函数去查找父类的方法,它就会自动按照MRO顺序去搜索父类的相关方法,并且自动避免重复调用的问题。
MRO详解
例4:

>>> class A:
	def __init__(self):
		print("哈喽,我是A")

>>> class B1(A):
	def __init__(self):
		super().__init__()
		print("哈喽,我是B1")

		
>>> class B2(A):
	def __init__(self):
		super().__init__()
		print("哈喽,我是B2")

		
>>> class C(B1,B2):
	def __init__(self):
		B1.__init__(self)
		B2.__init__(self)
		print("哈喽,我是C")
	
>>> C.mro()
[<class '__main__.C'>, <class '__main__.B1'>, <class '__main__.B2'>, <class '__main__.A'>, <class 'object'>]
>>> 
>>> B1.mro()
[<class '__main__.B1'>, <class '__main__.A'>, <class 'object'>]
>>> C.__mro__
(<class '__main__.C'>, <class '__main__.B1'>, <class '__main__.B2'>, <class '__main__.A'>, <class 'object'>)
>>> 

例5:
无可否认,利用了 MRO 顺序的 super() 非常优秀,它可以避免诸如钻石继承中多次访问相同父类的问题:

>>> class A:
...     def __init__(self):
...         print("哈喽,我是A~")
...
>>> class B1(A):
...     def __init__(self):
...        super().__init__()
...        print("哈喽,我是B1~")
...                
>>> class B2(A):
...     def __init__(self):
...         super().__init__()
...         print("哈喽,我是B2~")
...
>>> class C(B1, B2):
...     def __init__(self):
...         super().__init__()
...         print("哈喽,我是C~")
...
>>> c = C()
哈喽,我是A~
哈喽,我是B2~
哈喽,我是B1~
哈喽,我是C~

从钻石继承解决冲突这个角度来看,super() 无疑是正确且正义的!

课后题:
1.Python 中的构造函数(init()),到底是函数还是方法?
答:方法。
解析:在面向对象的编程语言中,我们习惯将对象的初始化方法称之为构造函数或构造器。但我们也知道,Python 中函数和方法的区别是,是否通过第一个参数绑定了相关对象,从这一点上来看,严谨的说 init() 应该称之为“构造方法”。
2.如果按照下面代码的方式去定义一个类,其实是存在一定风险的,你能指出问题所在,并给予修改的意见吗?

>>> class C:
...     x = []
...     def add_x(self, x):
...         self.x.append(x)

答:这里说的 “风险”,其实指的是将 x 定义为类属性这个事儿。
如果将代码改为下面这样,整体上会更好一些:

>>> class C:
...     def __init__(self):
...         self.x = []
...     def add_x(self, x):
...         self.x.append(x)
...
>>> c = C()
>>> d = C()
>>> c.add_x(250)
>>> d.add_x(520)
>>> c.x
[250]
>>> d.x
[520]

解析:类讲究共享,对象追求独立
3.请问下面代码中,为什么 class C(A, B) 就会报错,而 class C(B, A) 则不会报错呢?

>>> class A:
...     pass
...
>>> class B(A):
...     pass
...
>>> class C(A, B):
...     pass
...
Traceback (most recent call last):
  File "<pyshell#49>", line 1, in <module>
    class C(A, B):
TypeError: Cannot create a consistent method resolution
order (MRO) for bases A, B
>>> 
>>> # 改成下面这样就不报错
>>> class C(B, A):
...     pass
...

答:如果写成 class C(A, B) 则会造成冲突,因为根据继承优先级逻辑,C 是先去找 A 再去找 B,此时 A 的优先级比 B 高,但 B 又是继承自 A 的,所以从这个角度来看,B 的优先级又比 A 要高了。
4.请问下面代码会打印什么呢?

>>> class A:
...     x = 250
...        
>>> class B(A):
...     pass
...
>>> class C(B):
...     pass
...
>>> class D:
...     x = 520
...        
>>> class E(C, D):
...     pass
...
>>> E.x
>>> # 请问这里会打印什么内容?

答:250
解析:根据代码的继承关系,梳理如下图~
在这里插入图片描述
按照 MRO 规则,继承顺序应该是 E -> C -> B -> A -> D

>>> E.mro()
[<class '__main__.E'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class '__main__.D'>, <class 'object'>]

所以,由于只有类 A 和 D 有定义 x,且 A 的优先级比 D 要高,所以拿到的结果是 A.x。
对 MRO 规则不清晰,建议务必要认真看一下这篇文章MRO详解
5.请问下面代码会打印什么呢?

>>> class A:
...     x = 250
...     def add_x(self):
...         self.x += 1
...
>>> class B(A):
...     x = 300
...     def add_x(self):
...         A.add_x(self)
...
>>> class C(A):
...     x = 500
...     def add_x(self):
...         A.add_x(self)
...
>>> class D(B, C):
...     pass
...
>>> d = D()
>>> d.add_x()
>>> d.x
>>> # 请问这里会打印什么内容?

答:

>>> d.x
301

解析:

  • 先确定 x 的值,根据继承优先级顺序关系(MRO),由于类 D 没有定义 x,所以使用的是类 B 的 x 值;
  • 然后 add_x() 方法调用的是谁的呢?同样的道理,根据 MRO 顺序来查找,D 中没有,那么就到 B 中去找,B 中有 add_x() 的定义,不过它调用的是 A.add_x(),所以,最终执行的是 A 里面的 add_x() 方法。
    6.上面代码中的 A.add_x(self),如果替换成 super() 函数,应该怎么写?
    答:应该是 super().add_x()。
    解析:如果使用了 super(),就不必自己传递 self 参数
    7.请问下面代码会打印什么呢?
>>> class A:
...     def ping(self):
...         print("A ping~")
...
>>> class B(A):
...     def pong(self):
...         print("B pong~")
...
>>> class C(A):
...     def pong(self):
...         print("C pong~")
...
>>> class D(B, C):
...     def pingpong(self):
...         self.ping()
...         self.pong()
...
>>> d = D()
>>> d.pingpong()
>>> # 请问这里会打印什么内容?

答:

>>> d.pingpong()
A ping~
B pong~

解析:

  • 由于类 B、C、D 均没有实现 ping() 方法,所以 self.ping() 根据继承关系,会去调用类 A 的 ping() 方法。
  • 由于类 B、C 都有实现 pong() 方法,根据 MRO 顺序类 B 的覆盖优先级比类 C 高,所以 self.pong() 会去调用类 B 的 pong() 方法。

题目来自小甲鱼类和对象(III)

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值