从0.1开始学Python——[21]
学完封装之后,类的知识里面还有一个内容,那就是继承。顾名思义,继承指的是从别人能力拿来他的东西,让这样东西继续发挥作用。
继承
举个例子,一个类里面有两个功能(方法),然后我后来想再建一个类,但是类里面想好的方法涵盖了这个建好的类里面的方法,只是具体内容有所出入,那我们可以直接修改已经建好的类么?不现实,因为违反之前讲到的OCP原则。那直接复制再加上自己的方法创一个新的类呢?效率低,Python主打的就是精简,复制粘贴岂不是最繁琐的表现,那综上所述,我们需要用继承,来省去繁琐的重复部分。使用方法也很简单,之前提到可填可不填的父类(也叫超类、super、基类等)就是继承的方法。而继承父类东西的新类就是子类(衍生类),可以直接继承父类的所有属性和方法。
class Dongwufriends():
def biejiao(self):
print('动物别叫')
def bizui(self):
print('动物闭嘴')
class Xuebao(Dongwufriends):
def zhishi(self):
print('芝士雪豹')
xb = Xuebao()
xb.zhishi()
xb.biejiao()
芝士雪豹
动物别叫
就像这样。
由此可见,继承是扩展新建的类的功能的常用方法。另外,继承后的类创建的对象,既属于子类又属于父类。
print(isinstance(xb,Xuebao))
print(isinstance(xb,Dongwufriends))
True
True
子类的子类也从属于一开始的父类。
isinstance()函数和issubclass()函数
当不填父类的时候,父类默认为object,也就是说所有类都继承自object。issubclass(对象1,对象2)函数就是用来判断前者(对象1)是不是后者(对象2)子类的。而之前讲到的isinstance(对象1,对象2)函数则是判断实例(对象1)是不是属于类(对象2)的函数。
print(issubclass(Xuebao,Dongwufriends))
print(issubclass(Dongwufriends,object))
True
True
方法的重写
当子类和父类里面有相同的方法的时候,调用时会调用子类的方法,这个叫做方法的重写。
class Dongwufriends():
def biejiao(self):
print('动物别叫')
def bizui(self):
print('动物闭嘴')
class Xuebao(Dongwufriends):
def zhishi(self):
print('芝士雪豹')
def bizui(self):
print('雪豹闭嘴')
xb.bizui()
可以看到子类Xuebao()里面也有bizui(self)这个方法,但是和父类里面的方法内容不同,我们调用后发现:
雪豹闭嘴
结果是调用子类方法的结果。
如果是套娃,父类的子类还有子类等情况,而且调用了最小子类的某个方法时,将会按照从最小子类依次向上面最近父类查找的顺序找这个方法,先找到哪个就用哪个,找不到就报错。
class Dongwufriends():
def biejiao(self):
print('动物别叫')
def bizui(self):
print('动物闭嘴')
class Xuebao(Dongwufriends):
def zhishi(self):
print('芝士雪豹')
def bizui(self):
print('雪豹闭嘴')
class Ou(Xuebao):
def hhh(self):
pass
xb = Ou()
xb.biejiao()
xb.bizui()
动物别叫
雪豹闭嘴
super()方法
父类的所有方法,包括特殊方法,都会被子类继承。所以,涉及到初始化变量这种会需要新建的对象进行赋值操作的特殊方法,会导致新建子类的对象也需要赋值,即使子类没有初始化变量这样的特殊方法。
class Dongwufriends():
def __init__(self,name):
self._name = name
def biejiao(self):
print('动物别叫')
def bizui(self):
print('动物闭嘴')
@property
def name(self):
return self._name
@name.setter
def name(self,name):
self._name = name
class Xuebao(Dongwufriends):
def zhishi(self):
print('芝士雪豹')
def bizui(self):
print('雪豹闭嘴')
class Ou(Xuebao):
def hhh(self):
pass
xb = Ou()
Traceback (most recent call last):
File "D:/4classcode/4classcode/lianxi.py", line 758, in <module>
xb = Ou()
TypeError: __init__() missing 1 required positional argument: 'name'
xb = Ou('雪豹')
print(xb.name)
雪豹
可见只有对xb进行赋值name属性的操作以后才能解决问题,想要子类有其他属性,可以重写特殊方法。但是,当子类里面有其他父类没有的属性,而父类里面初始化属性太多的时候,子类属性就会写很多重复的。而你不写这些重复属性对应形参,也会有问题,那就是定义子类对应新对象的时候,你不能输入这些父类的属性,自然也无法获取这些属性的值。
上略
class Ou(Xuebao):
def __init__(self,color):
self._color = color
xb = Ou('雪豹','白')
Traceback (most recent call last):
File "D:/4classcode/4classcode/lianxi.py", line 758, in <module>
xb = Ou('雪豹','白')
TypeError: __init__() takes 2 positional arguments but 3 were given
可见输入name属性值导致错误。
xb = Ou('白')
这样就不会报错,但是对象xb是注定没有name属性的。
print(xb.name)
Traceback (most recent call last):
File "D:/4classcode/4classcode/lianxi.py", line 759, in <module>
print(xb.name)
File "D:/4classcode/4classcode/lianxi.py", line 742, in name
return self._name
AttributeError: 'Ou' object has no attribute '_name'
解决方法是在子类里面调用父类.初始化变量特殊方法(self,其他属性),但是父类写上去无法更改,更改子类的父类的时候不方便。
所以为了解决这种问题,使用super()方法,用来动态获取当前类的父类,而且这个方法不用传递self变量。
前略
class Ou(Xuebao):
def __init__(self,name,color):
super().__init__(name)
self._color = color
xb = Ou('雪豹','白')
print(xb.name)
雪豹
多重继承
在很多情况下,一个类可以有好多父类,一个类也可以有很多子类。后面这个情况的效果可以通过定义很多类然后填入相同父类来实现,而前面的情况则可以在定义某个类的时候把多个类写在父类里面,这就是多重继承,这时这个类可以继承所有父类的方法和属性。获取一个类的所有父类,可以采用**类名.(双下划线)bases(双下划线)**的方法。
class Ou(Xuebao,Dongwufriends):
def __init__(self,name,color):
super().__init__(name)
self._color = color
print(Ou.__bases__)
(<class '__main__.Xuebao'>, <class '__main__.Dongwufriends'>)
可以看出这样写Ou类的父类就是Xuebao(),Dongwufriends()两个。不过多重继承没必要频繁使用,因为不同父类里面有相同名方法的时候,只会以括号里前面书写的父类为准去使用,只有前面的没有这个名字的方法,才找后面的有没有。
xb.bizui()
雪豹闭嘴
另外,前面书写的父类自己如果还有父类,会先找它的父类,然后找后面书写的父类以及它的父类。也就是,调用方法:先找类自己->找第一个书写的父类->找第一个书写的父类的父类,如果也是多父类也按照这个套娃的顺序找->找第二个书写的父类->找第二个书写的父类的父类,如果也是多父类也按照这个套娃的顺序找->以此类推。在哪一部找到了,就使用这个方法。