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:
>>> 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()
[, , , , ]
>>>
>>> B1.mro()
[, , ]
>>> C.__mro__
(, , , , )
>>>
课后题:
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 "", line 1, in
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()
[, , , , , ]
所以,由于只有类 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
解析:
>>> 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~
解析:
题目来自小甲鱼类和对象(III)