继承与派生
1 什么是继承
是一种新建类的方式,新建的类称为子类,子类会遗传父类的属性,可以减少代码冗余在python中,子类(派生类)可以继承一个或者多个父类(基类,超类)
class Parent1: #定义父类 pass class Parent2(object): #定义父类 pass class Sub1(Parent1): #单继承,基类是ParentClass1,派生类是SubClass pass class Sub2(Parent1,Parent2): #python支持多继承,用逗号分隔开多个继承的类 pass print(Sub1.__bases__)#__base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类 print(Sub2.__bases__) print(Parent1.__bases__) print(Parent2.__bases__)
2 经典类与新式类
1.只有在python2中才分新式类和经典类,python3中统一都是新式类
2.在python2中,没有显式的继承object类的类,以及该类的子类,都是经典类
3.在python2中,显式地声明继承object的类,以及该类的子类,都是新式类
4.在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类
3 属性查找
先从自己类中查找,再然后再去父类中找...直到最顶级的父类。
#属性查找 class Foo: def f1(self): print('Foo.f1') def f2(self): #self=obj print('Foo.f2') self.f1() #obj.f1() class Bar(Foo): def f1(self): print('Bar.f1') obj=Bar() print(obj.__dict__) obj.f2() '''{} Foo.f2 Bar.f1 '''
4 子类重用父类的方法
#方法一(不建议用) class OldboyPeople: school = 'Oldboy' def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex def tell_info(self): print('<名字:%s 年龄:%s 性别:%s>' %(self.name,self.age,self.sex)) class OldboyStudent(OldboyPeople): def __init__(self,name,age,sex,course,stu_id): OldboyPeople.__init__(self,name,age,sex)#调用父类的方法 self.course=course self.stu_id=stu_id def learn(self): print('%s is learning' %self.name) def tell_info(self): print('我是学生:',end='') # self.tell_info() #stu1.tell_info() OldboyPeople.tell_info(self) #调用父类的方法 stu1=OldboyStudent('牛榴弹',18,'male','Python',1) stu1.tell_info()
#方法二:使用super关键字 class OldboyPeople: school = 'Oldboy' def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex def tell_info(self): print('<名字:%s 年龄:%s 性别:%s>' %(self.name,self.age,self.sex)) class OldboyStudent(OldboyPeople): def __init__(self,name,age,sex,course): # OldboyPeople.__init__(self,name,age,sex) super(OldboyStudent,self).__init__(name,age,sex)#使用super关键字 self.course=course def tell_info(self): print('我是学生: ',end='') # OldboyPeople.tell_info(self) super(OldboyStudent,self).tell_info() #使用super关键字 stu1=OldboyStudent('egon',18,'male','python') # print(stu1.name,stu1.age,stu1.sex,stu1.course) stu1.tell_info()
5 继承的实现原理
1、继承顺序
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中才分新式类与经典类
2、继承原理
为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
>>> F.mro() #等同于F.__mro__ [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>
]
6 子类中调用父类的方法
#方法一:指名道姓,即父类名.父类方法() #_*_coding:utf-8_*_ class Vehicle: #定义交通工具类 Country='China' def __init__(self,name,speed,load,power): self.name=name self.speed=speed self.load=load self.power=power def run(self): print('开动啦...') class Subway(Vehicle): #地铁 def __init__(self,name,speed,load,power,line): Vehicle.__init__(self,name,speed,load,power) self.line=line def run(self): print('地铁%s号线欢迎您' %self.line) Vehicle.run(self) line13=Subway('中国地铁','180m/s','1000人/箱','电',13) line13.run()
#方法二:super() class Vehicle: #定义交通工具类 Country='China' def __init__(self,name,speed,load,power): self.name=name self.speed=speed self.load=load self.power=power def run(self): print('开动啦...') class Subway(Vehicle): #地铁 def __init__(self,name,speed,load,power,line): #super(Subway,self) 就相当于实例本身 在python3中super()等同于super(Subway,self) super().__init__(name,speed,load,power) self.line=line def run(self): print('地铁%s号线欢迎您' %self.line) super(Subway,self).run() class Mobike(Vehicle):#摩拜单车 pass line13=Subway('中国地铁','180m/s','1000人/箱','电',13) line13.run()
即使没有直接继承关系,super仍然会按照mro继续往后查找
#A没有继承B,但是A内super会基于C.mro()继续往后找 class A: def test(self): super().test() class B: def test(self): print('from B') class C(A,B): pass c=C() c.test() #打印结果:from B print(C.mro()) #[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
#指名道姓 class A: def __init__(self): print('A的构造方法') class B(A): def __init__(self): print('B的构造方法') A.__init__(self) class C(A): def __init__(self): print('C的构造方法') A.__init__(self) class D(B,C): def __init__(self): print('D的构造方法') B.__init__(self) C.__init__(self) f1=D() #A.__init__被重复调用 ''' D的构造方法 B的构造方法 A的构造方法 C的构造方法 A的构造方法 ''' #使用super() class A: def __init__(self): print('A的构造方法') class B(A): def __init__(self): print('B的构造方法') super(B,self).__init__() class C(A): def __init__(self): print('C的构造方法') super(C,self).__init__() class D(B,C): def __init__(self): print('D的构造方法') super(D,self).__init__() f1=D() #super()会基于mro列表,往后找 ''' D的构造方法 B的构造方法 C的构造方法 A的构造方法 '''
当你使用super()函数时,Python会在MRO列表上继续搜索下一个类。只要每个重定义的方法统一使用super()并只调用它一次,那么控制流最终会遍历完整个MRO列表,每个方法也只会被调用一次(注意注意注意:使用super调用的所有属性,都是从MRO列表当前的位置往后找,千万不要通过看代码去找继承关系,一定要看MRO列表)