继承
面向对象的三大特性:继承、多态、封装。
一个类:可以被多个类继承;
一个类,可以继承多个类,这是python独有
什么是继承
继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类。
class A:pass #被称为父类、基类、超类 class B:pass class A_son(A,B):pass#被称为子类或者派生类 class AB_son(A,B):pass#注意前几节课的时候,讲过在定义类的时候都不带括号,这里可以明白,加括号以后就是父类 print(A_son.__bases__)#查找A_son的继承关系 print(AB_son.__bases__)#查找AB_son的继承关系 print(A.__bases__)#由于A在当前页面中它是父类,所以求他自身的父类就是对象'object',也就是说'object'是所有类的父类,当然这个结果只在python3中有 #所以开头的class A:就等价于class A(object),只不过object被省略
继承与抽象(先抽象再继承)
抽象
抽象即抽取类似或者说比较像的部分。
抽象分成两个层次:
1.将奥巴马和梅西这俩对象比较像的部分抽取成类;
2.将人,猪,狗这三个类比较像的部分抽取成父类。
抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度)
可以形象的理解为:抽象就是将具体的事物不断的抽象成一个一个的大类,一个一个的大类又可以被抽象成其他的大类,这个过程就是抽象。
继承:
继承是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。
抽象只是分析和设计的过程中,一个动作或者说一种技巧,通过抽象可以得到类。
可以形象的理解为:将一个大类“实例化”多个小类的过程就是继承,而将类实例化成具体的事物的过程就是真实的实例化。
那么继承到底有什么作用呢?
以人狗大战的代码为例,下面是对人和狗代码初始化的代码
class Person: def __init__(self,name,blood,agger,sex): self.name=name self.blood = blood self.agger = agger self.sex = sex def attack(self,Dog): Dog.blood=Dog.blood-self.agger print('%s被咬了,掉了%s点血'%(self.name,Dog.agger)) class Dog: def __init__(self,name,blood,agger,sex): self.name = name self.blood = blood self.agger = agger self.sex = sex
如果仔细观察,会发现任何狗的代码里面有重复的地方,可否将这些重复的地方合起来,减少代码量?答案是显而易见的
class Animal:#定义一个父类,将子类中所有重复的代码可以放在这里,然后下面如果使用再进行调用 def __init__(self,name,blood,agger,sex): self.name=name self.blood = blood self.agger = agger self.sex = sex class Person(Animal):#通过继承的方式,继承父类的相关方法 def attack(self,Dog): Dog.blood=Dog.blood-self.agger print('%s被咬了,掉了%s点血'%(self.name,Dog.agger)) class Dog(Animal):
显而易见,通过继承来写代码,代码的数量明显减少。使整个页面简洁。
单继承
class Animal: def __init__(self): print('执行Animal.__init__') self.func() def eat(self): print('%s eating'%self.name) def drink(self): print('%s drinking'%self.name) def func(self): print('Animal.func') class Dog(Animal): def gurad(self): print('gurading') def func(self): print('Dog.func')#当运行代码的时候执行的使这里的def func(self),而非父类Animal中的def func(self)。主要过程可这么理解,首先当执行dog这个类的时候,自身没有 __init__(self), #那么就去他的父类Animal中找,且父类中的self也是Dog这个类中的self,所以self.func()中的self也是Dog中的self,所以结果是'Dog.func'而非'Animal.func' dog=Dog() class Bird(Animal): def __init__(self,name): self.name = name def lay(self): print('laying')
进阶,继续以人狗大战的代码为例
class Animal: def __init__(self,name,blood,agger): self.name=name self.blood = blood self.agger = agger class Person(Animal): def __init__(self,name,blood,agger,sex): self.sex = sex def attack(self,Dog): Dog.blood=Dog.blood-self.agger print('%s被咬了,掉了%s点血'%(self.name,Dog.agger)) class Dog(Animal): def __init__(self,name,blood,agger,kind):#由于Dog类中已经有了__init__初始化,那么即使有了父类,也不会去父类中在进行初始化,所以当对Dog实例化以后,执行JACK.name,代码报错 self.kind = kind JACK = Dog('jack', 1000, 100, 'Siberian husky') print(JACK.name)#由于再Dog
进一步修改
class Animal: def __init__(self,name,blood,agger): self.name=name self.blood = blood self.agger = agger def eat(self): print('吃药回血') self.blood+=100 class Person(Animal): def __init__(self,name,blood,agger,sex): Animal.__init__(self, name, blood, agger) self.sex = sex def attack(self,Dog): Dog.blood=Dog.blood-self.agger print('%s被咬了,掉了%s点血'%(self.name,Dog.agger)) class Dog(Animal): def __init__(self,name,blood,agger,kind):#由于Dog类中已经有了__init__初始化,那么即使有了父类,也不会去父类中在进行初始化,所以当对Dog实例化以后,执行JACK.name,代码报错 Animal.__init__(self,name,blood,agger)#这里调用 Animal的__init__属性,同时也直接将Dog的self传给Animal,这样在调用Animal类中的属性的时候,就直接调用的是Dog的self self.kind = kind#这个属性是dog独有的属性,所以也叫做派生属性 def eat(self):#在Dog类中也定义一个eat方法,这个方法执行后,表示狗会长两颗牙。由于父类中也有eat方法,子类中这里也有,所以要想同时执行这两个eat方法,就需要 Animal.eat(self) self.teeth=2 JACK = Dog('jack', 1000, 100, 'Siberian husky') Alex=Person('alex', 1000, 100, 'Siberian husky') print(JACK.name) JACK.eat() print(JACK.blood)#调用Animal父类中的eat共用方法增加了JACK的血量 print(JACK.teeth) D:\anoconda\python.exe F:/python/python学习/人工智能/第一阶段day2/3.二分查找算法.py jack 吃药回血 1100 2 Process finished with exit code 0
super
super是另外一种来在子类中调用父类的方法
在子类内部调用
class Animal: def __init__(self,name,blood,agger): self.name=name self.blood = blood self.agger = agger def eat(self): print('吃药回血') self.blood+=100 class Person(Animal): def __init__(self,name,blood,agger,sex): # Animal.__init__(self,name, blood, agger)#等价于下面这段代码 super().__init__( name, blood, agger)#注意这里本来是super(Dog,self)但是Dog和self被省略;其次super只能在新式类中有用,python3中所有的类都是新式类 self.sex = sex def attack(self,Dog): Dog.blood=Dog.blood-self.agger print('%s被咬了,掉了%s点血'%(self.name,Dog.agger)) Alex=Person('alex', 1000, 100, 'Siberian husky') print(Alex.name)
在子类外部调用
class Animal: def __init__(self,name,blood,agger): self.name=name self.blood = blood self.agger = agger def eat(self): print('吃药回血') self.blood+=100 class Person(Animal): def __init__(self,name,blood,agger,sex): # Animal.__init__(self,name, blood, agger)#等价于下面这段代码 super().__init__( name, blood, agger)#注意这里本来是super(Dog,self)但是Dog和self被省略;其次super只能在新式类中有用,python3中所有的类都是新式类 self.sex = sex def attack(self,Dog): Dog.blood=Dog.blood-self.agger print('%s被咬了,掉了%s点血'%(self.name,Dog.agger)) def eat(self):print('eating') Alex=Person('alex', 1000, 100, 'Siberian husky') print(Alex.name) super(Person,Alex).eat()#这里是super在类的外部使用,执行的是父类中的eat函数,必须要要传递类名和对象名 Alex.eat()#这里执行的是子类中的eat函数
正常的代码中,单继承是减少了代码的重复
继承表达的是一种子类和父类的关系
多继承:
导引
class A: def func(self):print('A') class B: def func(self):print('B') class C: def func(self):print('C') class D(A,B,C): def func(self):print('D') d=D() d.func() D:\anoconda\python.exe F:/python/python学习/人工智能/第一阶段day2/3.二分查找算法.py D Process finished with exit code 0
如上图中代码所示,当第一次执行d.func()的时候,结果是D,但是如果注释def func(self):print('D'),ze
class A: def func(self):print('A') class B: def func(self):print('B') class C: def func(self):print('C') class D(A,B,C):pass # def func(self):print('D') d=D() d.func() D:\anoconda\python.exe F:/python/python学习/人工智能/第一阶段day2/3.二分查找算法.py A Process finished with exit code 0
结果是A.结论,当多继承的时候,如果当前类中没有,则从其括号中从左到右开始执行。
钻石继承问题:很经典
class A: def func(self):print('A') class B(A): def func(self):print('B') class C(A): def func(self):print('C') class D(B,C): def func(self):print('D') d=D() d.func()
代码的结果首先是执行D内部的函数,其次再依次是B、C中的函数,最后是A中的函数
原理:广度优先算法
mro()函数
class A: def func(self):print('A') class B(A): def func(self):print('B') class C(A): def func(self):print('C') class D(B,C): def func(self):print('D') d=D() d.func() print(D.mro())#能够形象的描述出继承执行的顺序 D:\anoconda\python.exe F:/python/python学习/人工智能/第一阶段day2/3.二分查找算法.py D [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
多继承中,我们子类的对象调用方法,默认是就近原则,找的顺序是广度优先
新式类中执行顺序:广优先
经典类执行顺序:深度优先
python2.7中,新式类和经典类共存,新式类要继承object
python3只有新式类,默认继承object
经典类和新式类还有一个区别,mro方法只在新式类中存在
super 只在python3中存在,其本质不是直接找父类,而是根据调用者的节点位置的广度优先顺序来的
class A: def func(self): print('A') class B(A): def func(self): super().func() print('B') class C(A): def func(self): super().func() print('C') class D(B,C): def func(self): super().func() print('D') d=D() d.func() print(D.mro())#能够形象的描述出继承执行的顺序 D:\anoconda\python.exe F:/python/python学习/人工智能/第一阶段day2/3.二分查找算法.py A C B D [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>] Process finished with exit code 0
注意上面super的执行结果
总结
继承:描述的是什么是什么的关系;
单继承:
先抽象再继承,几个类之间相同的代码抽象出来,成为父类,
子类自己没有的名字,就可以使用复利的方法和属性,
如果子类自己有,一定先用自己的,
在类中使用self的时候一定要看清楚self指向谁
多继承:
新式类和经典类:
新式类和经典类在python中本身存在,只是在继承这里的区分比较明显而已。
多继承寻找名字的顺序:新式类广度优先,经典类深度优先;
python3中有一个super方法,根据广度优先的继承顺序查找上一个类