一、继承大纲:
继承和抽象(两个概念)
1、单继承:
语法:
父类、基类、超类
子类、派生类
继承与重用:子类可以使用父类中的名字(变量和方法)
继承与派生:子类在父类的基础上又新创建了自己需要的方法和属性
父类有的子类没有 - 子类对象直接调用,就会直接执行父类的方法
父类有的子类也有 - 子类对象调用,直接执行子类中的方法
想在子类中使用父类的方法:
父类名.方法名(self,...)
super().方法名(....)
规范的编程模式:抽象类
2、多继承:
语法:
接口:Python 和 c++ 没有接口,只有多继承,c# 和 Java 才有接口
新式类和经典类:(继承了object的类就是新式类)
新式类中:(Python 3.x都是新式类)
所有的多继承关系寻找方法的顺序--->遵循广度优先算法
继承object
mro方法 # 查看多继承关系查找顺序(print(类名.mro()))
super:super不是单纯的找父类,而是遵循mro顺序的
经典类:(Python2.7以前都是经典类,2.7以后经典类与新式类并存)
Python 2.x
不主动继承object
经典类在找父类中方法的过程中--->遵循深度优先
不提供mro方法和super
二、什么是继承:
继承是一种创建新类的方式,在Python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类
Python中类的继承分为:单继承和多继承:
class ParentClass1: # 定义父类 pass class ParentClass2: # 定义父类 pass class SubClass1(ParentClass1): # 单继承,基类(父类)是ParentClass1,派生类(子类)是SubClass pass class SubClass2(ParentClass1,ParentClass2): # Python支持多继承,用逗号分隔开多个继承的类 pass
查看继承:
>>> SubClass1.__bases__ #__base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类 (<class '__main__.ParentClass1'>,) >>> SubClass2.__bases__ (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)
提示:如果没有指定基类,Python的类会默认继承object类,object是所有Python类的基类(老祖宗),它提供了一些常见方法(如__str__)的实现。
>>> ParentClass1.__bases__ (<class 'object'>,) >>> ParentClass2.__bases__ (<class 'object'>,)
三、继承与抽象(先抽象再继承)
1、抽象即抽取类似或者说比较像的部分。
抽象分成两个层次:
1、将奥巴马和梅西这俩对象比较像的部分抽取成类;
2、将人,猪,狗这三个类比较像的部分抽取成父类。
抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度)
2、继承:是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。
抽象只是分析和设计的过程中,一个动作或者说一种技巧,通过抽象可以得到类。
四、继承与重用性
在开发程序的过程中,如果我们定义了一个类A,然后又想新建立一个类B,但是类B的大部分内容与类A的相同时,我们不可能从头开始写一个类B,这就用到了类的继承的概念。
通过继承的方式新建类B,让B继承A,B会‘遗传’A的所有属性(数据类型和函数属性),实现代码重用
# 猫类 : # 属性 : 名字,品种,食物 # 方法 : 叫,抓老鼠,吃,喝 # 狗类 : # 属性 : 名字,品种,食物 # 方法 : 叫,看家,吃,喝 class Animal: def __init__(self,name,kind,food,language): self.name = name self.kind = kind self.food = food self.language = language def yell(self): print('%s叫' % self.language) def eat(self): print('吃%s' % self.food) def drink(self): print('喝水') class Cat(Animal): def catch_mouse(self): print('抓老鼠') class Dog(Animal): def look_after_house(self): print('看家') # 继承 # 父类/超类/基类 :Animal # 子类/派生类 :Cat、Dog # 继承与重用 - 父类中所有的属性和方法都可以被子类使用了 cat = Cat('阿猫','橘猫','牛杂','喵喵') print(cat.name) #阿猫 cat.drink() #喝水 cat.eat() #吃牛杂 cat.yell() #喵喵叫 dog = Dog('阿狗','土狗','阿猫','汪汪') print(dog.name) #阿狗 dog.drink() #喝水 dog.eat() #吃阿猫 dog.yell() #汪汪叫
提示:用已经有的类建立一个新的类,这样就重用了已经有的软件中的一部分设置大部分,大大生了编程工作量,这就是常说的软件重用,不仅可以重用自己的类,也可以继承别人的,比如标准库,来定制新的数据类型,这样就是大大缩短了软件开发周期,对大型软件开发来说,意义重大。
五、派生
子类在父类的基础上又创建了自己的新的方法和属性
当然子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),需要注意的是,一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了。
# 派生 class Animal: def __init__(self,name,kind,food,language): print('in animal') self.name = name self.kind = kind self.food = food self.language = language def yell(self): print('%s叫' % self.language) def eat(self): print('吃%s' % self.food) def drink(self): print('喝水') class Cat(Animal): # Animal的派生类 def __init__(self,name,kind,food,language,eye_color): print('in Cat') self.eye_color = eye_color # 派生属性 Animal.__init__(self,name,kind,food,language) # 需要手动添加self # super().__init__(name,kind,food,language) # 不需要手动添加self def catch_mouse(self): # 派生方法 print('抓老鼠') def eat(self): # 不仅执行了父类中的基础功能,还完成了特殊的功能 Animal.eat(self) # super().eat() self.weight = 10 class Dog(Animal): def look_after_house(self): print('看家') def eat(self): # Animal.eat(self) super().eat() self.drink() cat = Cat('阿猫','橘猫','牛杂','喵喵','绿色') print(cat.eye_color) # 绿色 print(cat.food) # 牛杂 cat.catch_mouse() # 抓老鼠 cat.eat() # 吃牛杂 print(cat.weight) # 10 # 当子类当中有要被调用的方法的时候,子类的对象会直接选择子类中的方法、变量,父类中的方法不会被自动执行 # 如果我们既想要执行子类的方法,也想要执行父类的方法,那么需要在子类的方法中调用父类的方法: # 父类名.方法名(self,...) # super().方法名(...) # 帮助我们在子类中调用父类中的同名方法
六、抽象类与接口类
接口类:
# java c# # 不允许多继承 # 接口 Interface 接口可以被多继承 # Interface FlyAnimal: # 规范继承我的类必须实现这个方法 # def fly():pass # Interface WalkAnimal: # def walk():pass # Interface SwimAnimal: # def swim():pass # class Tiger(WalkAnimal,SwimAnimal): 继承了一个规范 # def walk():代码 # def swim():代码
抽象类:只能被继承,不能被实例化(模板,规则)
# 工作中 公司有使用抽象类开发的规则 # 源码 别人使用抽象类 # 支付功能 from abc import ABCMeta,abstractmethod class Payment(metaclass=ABCMeta): # 模板的功能 @abstractmethod # abstractmethod是一个装饰器,装饰器怎么用?放在函数/类的上一行 def pay(self):pass @abstractmethod def shouqian(self):pass class Alipay(Payment): def pay(self,money): print('使用支付宝支付了%s元'%money) class Wechatpay(Payment): def pay(self,money): print('使用微信支付了%s元'%money) class ApplePay(Payment): def pay(self,money): print('使用applepay支付了%s元' % money) def pay(obj,money): obj.pay(money) p = Payment() # a = Alipay() # # a.pay(100) # pay(a,100) # # we = Wechatpay() # # we.pay(200) # pay(we,200) # # ap = ApplePay() # 规范 # 多人开发、复杂的需求、后期的扩展 # 手段 来帮助我们完成规范 # 抽象类 # 抽象类是一个规范,它基本不会实现什么具体的功能,抽象类是不能被实例化 # 要想写一个抽象类 # from abc import ABCMeta,abstractmethod # 在这个类创建的时候指定 metaclass = ABCMeta # 在你希望子类实现的方法上加上一个 @abstractmethod装饰器 # 使用抽象类 # 继承这个类 # 必须实现这个类中被@abstractmethod装饰器装饰的方法
七、钻石继承
class A(object): def test(self): print('from A') class B(A): def test(self): print('from B') class C(A): def test(self): print('from C') class D(B): def test(self): print('from D') class E(C): def test(self): print('from E') class F(D,E): # def test(self): # print('from F') pass f1=F() f1.test() print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性 #新式类继承顺序:F->D->B->E->C->A #经典类继承顺序:F->D->B->A->E->C #python3中统一都是新式类 #pyhon2中才分新式类与经典类 继承顺序
继承原理:
python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,例如
>>> F.mro() #等同于F.__mro__ [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>,
<class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
八、继承小结:
继承的作用:
1、提高代码的重用性
2、提高代码的可读性
3、规范编程模式
几个名词:
1、抽象:抽象即抽取类似或者说比较像的部分,是一个从具体到抽象的过程
2、继承:子类继承了父类的方法和属性
3、派生:子类在父类方法和属性的基础上产生了新的方法和属性
抽象类与接口类:
1、多继承问题:
在继承抽象类的过程中,我们应该尽量避免多继承;
而在继承接口的时候,我们反而鼓励你来多继承接口
2、方法的实现:
在抽象类中,我们可以对一些抽象方法做出基础实现;
而在接口类中,任何方法都只是一种规范,具体的功能需要子类实现
钻石继承:
新式类:广度优先
经典类:深度优先