什么时候用继承?
1.当类之间有显著不同,且较小的类是较大的类的组件时,用组合比较好
比如机器人类是一个大类,躯干类、胳膊类、腿类是小类
2.当类之间有很多相同的属性与功能,提取这些共性作为基类,用继承比较好
比如鸡和鸭,我们可以提取他们的共性(两只翅膀、用两只脚走路)做一个禽类,但是他们也有各自独特的个性(鸡会打鸣,鸭会游泳)
class qing(): #共性提取为一个基类(禽类) wing = 2 #父类数据属性 def __init__(self,owner): self.owner = owner def walk(self): #父类函数属性 print(‘%s用两只脚走路‘ %self.owner) class ji(qing): #鸡类继承禽类 def da_ming(self): #子类有自己的函数属性 print(‘%s会打鸣‘ %self.owner) class ya(qing): #鸭类继承禽类 def you_yong(self): #子类有自己的函数属性 print(‘%s会游泳‘ %self.owner) j1 = ji(‘小王家的鸡‘) #鸡类实例化 j1.da_ming() #可以调用实例自己的函数属性 j1.walk() #可以调用实例继承来的函数属性 y1 = ya(‘小明家的鸭‘) #鸭类实例化 y1.you_yong() y1.walk()结果:
小王家的鸡会打鸣
小王家的鸡用两只脚走路
小明家的鸭会游泳
小明家的鸭用两只脚走路
继承有两种含义
含义一:子类继承基类的方法,并且做出自己的改变或扩展(代码重用),如上禽类的例子
含义二:基类是一个接口类,子类继承这个接口类,并实现接口中定义的方法
实践中,继承的第一种含义意义并不大,甚至是有害的,因为这种方式使得子类与基类出现强耦合
继承的第二种含义就很重要了,它又叫“接口继承”
接口继承实质上做了一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可以一视同仁的处理实现了接口的所有对象——程序设计上,这叫归一化
就像linux的泛文件概念(一切皆文件),所有东西都可以当文件处理,不必关心它是内存、磁盘还是网络(然后在内存、磁盘和网络的子类中,再去做针对性的底层设计,即具体实现接口类中定义的接口)
如何强制子类必须实现基类中某些指定的方法和属性,否则就抛异常?python的abc模块就满足了这个需求。
import abc class All_file(metaclass=abc.ABCMeta): #abc.ABCMeta是一个用于实现抽象类的一个基础类 @abc.abstractmethod #加上@abstractmethod之后,如果需要用到的这个方法则必须用新的方法将其实现 def read(self): #子类必须实现读方法 pass #接口类不用实现逻辑,只是用来规范子类 @abc.abstractmethod #子类必须实现写方法 def write(self): pass class Disk(All_file): def read(self): print(‘disk read‘) def write(self): print(‘disk write‘) class Cdrom(All_file): def read(self): print(‘cdrom read‘) def write(self): print(‘cdrom write‘) class Mem(All_file): def read(self): print(‘mem read‘) def write(self): #如果这个write方法不写,将报错:TypeError: Can‘t instantiate abstract class Mem with abstract methods write print(‘mem write‘) m1=Mem() m1.read() #mem read m1.write() #mem write
1.接口类的作用只是用来规范子类,所以他不用实现逻辑,写个pass就ok
2.接口类不需要被实例化,也没有这个必要
3.子类如果继承了接口类,就一定要实现接口类中加了@abstractmethod的方法
继承的顺序
python的类可以继承多个类,如果继承多个类,那么其继承顺序的方式有:深度优先和广度优先
基类或者父类继承了object类,那么该类就是新式类,否则便是经典类
注意:python3中统一都是新式类,pyhon2中才分新式类与经典类
当类是经典类时,多继承情况下,会按照深度优先方法查找
当类时新式类时,多继承情况下,会按照广度优先方法查找
class A: def test(self): print(‘A‘) pass class B(A): def test(self): print(‘B‘) pass class C(A): def test(self): print(‘C‘) pass class D(B): def test(self): print(‘D‘) pass class E(C): def test(self): print(‘E‘) pass class F(D,E): def test(self): print(‘F‘) pass f1=F() #python3都是新式类,广度优先,所以继承顺序是:F--D--B--E--C--A f1.test() print(F.__mro__) #(<class ‘__main__.F‘>, <class ‘__main__.D‘>, <class ‘__main__.B‘>, <class ‘__main__.E‘>, <class ‘__main__.C‘>, <class ‘__main__.A‘>, <class ‘object‘>)
B.C继承A,D继承B,E继承C,F继承D,E,我们看下F的继承顺序:
如果是新式类,继承的顺序是:F--D--B--E--C--A,如图中绿线所示,左边的线不会找到头,这就是所谓的广度优先
如果是经典类,继承的顺序是:F--D--B--A--E--C,如图中红线所示,左边的线直接找到头,这就是所谓的深度优先
Python到底是如何实现继承的?
对于定义的每一个类,Python会计算一个方法解析顺序列表(MRO列表),这个MRO列表就是一个简单的所有基类的线性顺序列表
为了实现继承,Python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止
MRO列表遵循以下三条准则:
- 子类会先于父类被检查;
- 多个父类会根据它们在列表中的顺序被检查;
- 如果对下一个类存在两个合法的选择,则选择第一个类;
注意:只有新式类才有.__mro__这个属性,可以查看线性列表,经典类没有这个属性
在子类中调用父类方法
class People: def __init__(self,name,age): self.name = name self.age = age def run(self): print(‘%s 在跑!‘%self.name) class China(People): def __init__(self,name,age,country): People.__init__(self,name,age) #子类中调用父类的__init__方法,可以用super().__init__(name,age) 代替,注意用super()调用父类的方法时不用传self self.country = country def run(self): People.run(self) #子类中调用父类的run方法 可以用super().run()代替 print(‘%s人 %s 在跑!‘%(self.country,self.name)) p2 = China(‘Tom‘,26,‘中国‘) p2.run()
原文地址:https://www.cnblogs.com/xulan0922/p/10339245.html