类的继承
继承
继承可以获取父类的属性和方法,减少代码的重用
在Python3中,所有的类都是继承object的,它可以称之为是所有类的父类,object是可以省略的
父类:被继承的类,就是父类,也叫作基类或者超类
子类:继承父类的类,就是子类
In [1]: class Base(): # 父类
...: def base_print(self):
...: print('base')
...:
In [4]: class A(Base): # 在类名后面加上括号,括号中是继承列表,可继承多个类
...: def a_print(self):
...: print('a')
...:
In [5]: a = A()
In [6]: a.a_print()
a
In [8]: a.base_print() # 直接通过子类的实例化,来调用父类的方法
base
类的继承方式:
- 凡是公有的方法或者属性,都是可以继承的
- 凡是私有的方法或者属性,都是不能继承的
- 原来的是什么,继承过来, 就还是什么
重写
当子类和父类有同名成员的时候,子类的成员会覆盖父类的同名成员
In [9]: class Base():
...: def print(self):
...: print('Base.print')
...:
In [10]: class Sub(Base): # 继承Base父类
...: def print(self): # 父类和子类同时拥有print方法
...: print('Sub.print')
...:
In [11]: sub = Sub() # 实例化子类
In [12]: sub.print() # 当调用父类和子类的相同的方法的时候,则会调用子类的print方法
Sub.print
当重写后,有需求需要调用父类的同名方法的时候,可以使用super方法来调用
In [14]: class Sub(Base): # 继承上面的Base类
...: def print(self):
...: print('Sub.print')
...:
...: def foo(self):
...: super().print() # 使用super方法调用父类的print方法
...:
...:
In [15]: sub = Sub()
In [16]: sub.foo() # 结果集显示的确实是父类的print方法的返回值
Base.print
当父类中含有一个带参数的初始化方法的时候,子类一定需要一个初始化方法,并且在初始化方法中调用父类的初始化方法
In [17]: class Base: # 定义一个初始化方法中有带参数的父类
...: def __init__(self, a, b):
...: self.__a = a
...: self.__b = b
...: def sum(self):
...: return self.__a + self.__b
...:
In [18]: class Sub(Base):
...: def __init__(self,a,b,c):
...: self.c = c
...: super().__init__(a,b) # 给父类传参,这个必须要有的,要不然就报错
...:
In [19]: sub = Sub(1,2,3)
In [20]: sub.sum()
Out[20]: 3
super获取类变量
In [24]: class Base:
...: NAME = 'BASE'
...:
In [25]: class Sub(Base):
...: NAME = 'SUB'
...: def print(self):
...: print(self.NAME)
...: print(super(Sub,Sub).NAME) # 获取父类的属性
...:
In [26]: sub = Sub()
In [27]: sub.print()
SUB
BASE
super对象只能获取类的属性,是不能获取类实例的属性或者方法的
多继承
多继承的表达形式
In [2]: class Base:
...: pass
...:
In [3]: class Base1:
...: pass
...:
In [4]: class Sub2(Base, Base1): # 多继承,在继承列表中存在了多个类的时候,表示多继承
...: pass
...:
多继承会把继承列表中的所有的公有成员都继承过来
在多继承中,会出现继承列表中的方法重名的情况,因此就有一个MRO(方法查找顺序)
MRO两条特性
- 本地优先:自己定义或者重写的方法优先,按照继承列表,从左到右查找
- 单调性:所有子类也要满足查找顺序
按照继承列表,从左到右查找调用
In [13]: class A:
...: def print(self):
...: print('a')
...:
In [14]:
In [14]: class B:
...: def print(self):
...: print('b')
...:
...:
In [15]:
In [15]: class C(A,B):
...: pass
...:
In [16]: c = C()
In [17]: c.print() # print是继承列表中的重名方法,因为先继承的A因此,会先调用A的print方法
a
本地优先
In [5]: class C(A,B):
...: def print(self): # 因为在当前类中也存在print方法,因此会首先调用当前类中的print方法
...: print('c')
...:
In [6]: c = C()
In [7]: c.print()
c
单调性,每个类都有一个__mro__方法,可以查看类方法查找的顺序
In [8]: C.__mro__
Out[8]: (__main__.C, __main__.A, __main__.B, object)
从左到右,根据首先查找本地时候有重名的方法,然后根据继承的列表来查询方法,最后继承object
在python中是通过C3算法来确定是否满足mro的两个原则的
当一个类定义的时候,解释器会执行C3算法来确定mro。如果C3算法抛出异常,则此类不能定义
在我们的开发中,要尽量的避免使用多继承,因为多继承特别容易出现代码的混乱,当代码的继承出现多层的时候,就会特别容易出错
多继承会对程序的心智负担造成非常大的压力
Mixin
Mixin其实就是一种用组合的方式,将多个父类的方法糅合到子类中,让子类可以无缝的调用父类的方法
Mixin的组合方式,在理论上来说是优于继承的
当然Mixin也是有限制的
- Mixin方式的类不应该有初始化方法,因为是组合的因此,Mixin类重尽量不要有初始化方法,因为胡覆盖掉父类的初始化方法,发生不必须的错误
- Mixin方式的类不能独立工作,当Mixin方式的类,在独立的情况下,是无法工作的,如果可以工作,就已经不属于Mixin方式的类的范畴了
- Mixin方式的类的祖先也必须是Mixin的方式的类
通常情况下,Mixin类总是在继承列表的第一位
个人觉得还是尽量别使用Mixin方式来组合方法称为一个新的类来使用,因为当组合类越来越多的时候,就管理和排错我们的程序了,因此如何真的觉得Mixin方式非常适合现在的应用场景的话,在去使用,一般情况下,其实装饰器也是可以实现Mixin这样方式的组合类的,装饰器更方便与查找和管理。