1.继承
- 继承指的是类与类之间的关系,是一种什么“是”什么的关系,继承的功能之一就是用来解决代码重用问题
- 继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可以成为基类或超类,新建的类称为派生类或子类
- 单继承和多继承
-
1 class ParentClass1: 2 pass 3 class ParentClass2: 4 pass 5 class SubClass1(ParentClass1):#单继承 6 pass 7 class SubClass2(ParentClass1,ParentClass2):#多继承 8 pass 9 10 #查看继承 11 print(SubClass1.__bases__)#(<class '__main__.ParentClass1'>,) 12 print(SubClass2.__bases__)#(<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>) 13 #__bases__与__base__的区别 14 print(SubClass2.__base__)#<class '__main__.ParentClass1'>,只能查看从左到右继承的第一个子类
2.经典类与新式类
1 #在python2中,经典类:没有继承object的类,以及它的子类都称为经典类 2 # class Foo: 3 # pass 4 # class Bar(Foo): 5 # pass 6 #在python2中,新式类:继承object的类,以及它的子类都称为新式类 7 # class Foo(object): 8 # pass 9 # class Bar(Foo): 10 # pass 11 12 #在python3中,都是新式类,一个类没有继承object,默认继承object 13 # class Foo:#默认继承object, 14 # pass 15 # class Bar(Foo): 16 # pass 17 # print(Foo.__bases__)#(<class 'object'>,)
3.继承与抽象(先抽象再继承)
- 抽象:抽取类似或者说比较像的部分,抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度)
- 继承:是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构
- 抽象只是分析和设计的过程中,一个动作或者说一种技巧,通过抽象可以得到类
4.继承与重用性
- 在开发程序的过程中,如果已经定义了类A,又想新建立一个类B,但是类B的大部分内容与类A相同,可以通过继承的方式新建类B,让B继承A,B会“遗传”A的所有属性(数据属性和函数属性),实现代码重用,大大节省编程工作量。
- 常说的软件重用,不仅可以重用自己的类,也可以继承别人的,比如标准库,来定制新的数据类型,这样就大大缩短了软件开发周期,对大型软件开发来说,意义重大
-
1 #继承属性查找 2 class Foo: 3 def f1(self): 4 print("form Foo.f1") 5 def f2(self): 6 print("form Foo.f2") 7 self.f1() 8 class Bar(Foo): 9 def f1(self): 10 print("form Bar.f1") 11 b = Bar() 12 b.f2() 13 #form Foo.f2 #显而易见,不用解释 14 #form Bar.f1 #解释:在运行self.f1()时,其实就是b.f1(),所以按照继承顺序,优先从Bar类中找,而不是就近原则, 15 # 因此打印的是form Bar.f1 16 print(Bar.mro()) 17 #[<class '__main__.Bar'>, <class '__main__.Foo'>, <class 'object'>]
5.派生
- 子类可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),需要注意的是,一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了
- 在子类中,新建的重名的函数属性,在编辑函数内功能的时候,有可能需要重用父类中重名的那个函数功能,应该是用调用普通函数的方式,即:类名.func(),此时就与调用普通函数无异了,因此即便是self参数也要为其传值
-
1 class Riven(Hero): 2 camp='Noxus' 3 def __init__(self,nickname,aggressivity,life_value,skin): 4 Hero.__init__(self,nickname,aggressivity,life_value) #调用父类功能 5 self.skin=skin #新属性 6 def attack(self,enemy): #在自己这里定义新的attack,不再使用父类的attack,且不会影响父类 7 Hero.attack(self,enemy) #调用功能 8 print('from riven') 9 def fly(self): #在自己这里定义新的 10 print('%s is flying' %self.nickname)
6.继承的实现原理
- 对于定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表。
- 经典类:多继承情况下,在要查找属性不存在时,会按照深度优先的方式查找下去,没有mro()方法
- 新式类:多继承情况下,在要查找属性不存在时,会按照广度有限的方式查找下去,可通过mro()方式查询继承顺序
-
1 class A(object): 2 def test(self): 3 print('from A') 4 pass 5 6 class B(A): 7 def test(self): 8 print('from B') 9 pass 10 11 class C(A): 12 def test(self): 13 print('from C') 14 pass 15 16 class D(B): 17 def test(self): 18 print('from D') 19 pass 20 21 class E(C): 22 def test(self): 23 print('from E') 24 pass 25 26 class F(D,E): 27 def test(self): 28 print('from F') 29 pass 30 f1=F() 31 f1.test() 32 # print(F.mro())# F > D > B > E > C > A > object 33 #[<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, 34 # <class '__main__.C'>, <class '__main__.A'>, <class 'object'>] 35 print(F.__bases__)#(<class '__main__.D'>, <class '__main__.E'>) 36 37 #经典类没有提供mro()方法
- 为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。这个MRO列表的构造是通过一个C3线性算法来实现的,只在新式类中有,遵循如下三条准则
- 子类会先于父类被检查
- 多个父类会根据它们在列表中的顺序被检查
- 如果对下一个类存在两个合法的选择,选择第一个父类
7.在子类中调用父类的方法
- 方式一:指名道姓,即父类名.父类方法(),不依赖于继承
-
1 #方式一 2 class Vehicle:#定义交通工具类 3 country = "China" 4 def __init__(self,name,speed,load,power): 5 self.name = name 6 self.speed = speed 7 self.load = load 8 self.power = power 9 def run(self): 10 print("开动啦。。。") 11 class Subway(Vehicle): 12 def __init__(self,name,speed,load,power,line): 13 Vehicle.__init__(self,name,speed,load,power)#self一定要传 14 self.line = line 15 def run(self): 16 print('地铁%s号线欢迎您' %self.line) 17 Vehicle.run(self)#self一定要传 18 line13 = Subway('中国地铁','180m/s','1000人/箱','电',13) 19 line13.run() 20 # 地铁13号线欢迎您 21 # 开动啦。。。
- 方式二:super(),依赖于继承
-
1 #方式二 2 class Vehicle:#定义交通工具类 3 country = "China" 4 def __init__(self,name,speed,load,power): 5 self.name = name 6 self.speed = speed 7 self.load = load 8 self.power = power 9 def run(self): 10 print("开动啦。。。") 11 class Subway(Vehicle): 12 def __init__(self,name,speed,load,power,line): 13 # super(Subway, self).__init__(name,speed,load,power) 14 super().__init__(name,speed,load,power) 15 #super(Subway, self)就相当于实例本身,在python3中super()等于super(Subway, self) 16 self.line = line 17 def run(self): 18 print('地铁%s号线欢迎您' %self.line) 19 # super(Subway, self).run() 20 super().run() 21 line13 = Subway('中国地铁','180m/s','1000人/箱','电',13) 22 line13.run()
- 区别:方式一是跟继承没有关系的,而方式二的super()是依赖于继承的,并且即使没有直接继承关系,super依然会按照mro继续往后查找
-
1 class A: 2 def test(self): 3 super().test() 4 class B: 5 def test(self): 6 print("from B") 7 class C(A,B): 8 pass 9 10 c = C() 11 c.test()#from B 12 print(C.mro()) 13 # [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]