这一章节主要讲解面向对象高级编程->继承进阶篇,包括类多继承介绍和继承经典类和新式类属性的查找顺序不同之处。
多继承
上一章节我们讲到继承,子类继承父类,可以拥有父类的属性和方法,也可以进行扩展。但是有时候会发现,子类需要继承一个类的方法,又要继承另一个类的方法,才能完成要实现的功能。怎么办?python给我们提供了多继承的概念。类似于C++语言,俗称类的多继承。
看个例子:
>>> class Animal(object): def __init__(self,name): self.name = name >>> class Runable(object): pass >>> class Flyable(object): pass >>> class Dog(Animal,Runable): def __init__(self,name): super(Dog,self).__init__(name) >>> class Bird(Animal,Flyable): def __init__(self,name): super(Bird,self).__init__(name) >>> d = Dog(‘wangcai‘) >>> b = Bird(‘yingying‘)
声明了Animal类和Runable类,Flyable类。子类Dog因为即是动物,又具有run的能力。所以继承Animal类和Runable类。子类Bird因为即是动物,又具有fly的能力。所以继承Animal类和Runable类。
继承进阶
对于python语言来讲,继承可以分为单继承,多层继承,多重继承。
对于继承来讲,子类如果有构造函数__init__,不会自动调用父类的构造函数。如果子类没有自己的构造函数__init__,则会直接从父类继承构造函数.
>>> class Person(object): def __init__(self): print(‘Person class initing!‘) >>> class Student(Person): def __init__(self): print(‘Student class initing!‘) >>> s = Student() Student class initing!//子类实例化对象s,并不会自动调用父类的__init__。(一定区别于C++,JAVA,C#一些面向对象语言)
如果一定需要用到父类的构造函数,则需要在子类的构造函数中显式的调用。
>>> class Student(Person): def __init__(self): super(Student,self).__init__() //调用了父类的__init__ //或者也可以写成 Person.__init__(self) print(‘Student class initing!‘) >>> s = Student() Person class initing! Student class initing!
说明:super(type, obj),其中obj必须是type类型或者type子类类型的实例,否则会报错:
TypeError: super(type, obj): obj must be an instance or subtype of type
针对多层继承来讲,super的用法也是一样的。但是需要注意以下情况:
>>> class Person(object): def __init__(self): print(‘Person class initing!‘) >>> class Man(Person): def __init__(self): super(Man,self).__init__() print(‘Man class initing!‘) >>> class Teenager(Man): def __init__(self): super(Teenager,self).__init__() print(‘Teenager class initing!‘) >>> class Student(Teenager): def __init__(self): super(Student,self).__init__() print(‘Student class initing!‘) >>> s = Student() Person class initing! Man class initing! Teenager class initing! Student class initing!
如果Student类,super(Student,self)改为super(Man,self)会有什么结果输出?
>>> class Person(object): def __init__(self): print(‘Person class initing!‘) >>> class Man(Person): def __init__(self): super(Man,self).__init__() print(‘Man class initing!‘) >>> class Teenager(Man): def __init__(self): super(Teenager,self).__init__() print(‘Teenager class initing!‘) >>> class Student(Teenager): def __init__(self): super(Man,self).__init__() print(‘Student class initing!‘) >>> s = Student() Person class initing! Student class initing!
class Student(Teenager): def __init__(self): super(Teenager,self).__init__() print(‘Student class initing!‘) >>> s = Student() Person class initing! Man class initing! Student class initing!
可看出super(type[,type2_or_obj]),type决定了super调用方法所在的父类--type的父类(如果有的话),即type决定了前往哪个父类调用指定的方法。
那么super(Man,self)指的是 调用Man类父类的Person类的__init__方法。
刚才在介绍继承的时候,说过如果子类并没有自己的__init__方法,则会继承父类的__init__。那么如果是多重继承的话,子类继承了两个甚至更多的类,那么子类是继承哪个类的__init__方法?
>>> class FatherA(object): def __init__(self): print(‘FatherA class initing!‘) >>> class FatherB(object): def __init__(self): print(‘FatherB class initing!‘) >>> class Son(FatherA,FatherB): pass >>> s = Son() FatherA class initing!
将class Son(FatherA,FatherB)改为class Son(FatherB,FatherA)会有什么结果?
>>> class Son(FatherB,FatherA): pass >>> s = Son() FatherB class initing!
大家可以通过上面的例子,可以发现:
子类从多个父类派生,子类没有自己的构造函数时,
(1)按继承顺序,第一个父类而它又有自己的构造函数,就继承它的构造函数;
(2)如果最前面第一个父类没有构造函数,则继承第2个的构造函数,如果第2个类也没有,则继承第3个的。以此类推,最后会继承object。
针对于构造函数__init__,遵循上面的继承规则。那么实例方法是否也遵循上面的继承规则?
>>> class FatherA(object): def __init__(self): print(‘FatherA class initing!‘) def ft(self): print(‘FatherA ft fun!‘) >>> class FatherB(object): def __init__(self): print(‘FatherB class initing!‘) def ft(self,args): print(‘FatherB ft fun!‘) >>> class Son(FatherA,FatherB): def __init__(self): super(Son,self).ft() >>> s = Son() FatherA ft fun!
看起来实例方法也是遵循上面的规则,按继承顺序调用父类方法。
如果就是需要调用两个父类的ft方法怎么办?
>>> class Son(FatherA,FatherB): def __init__(self): FatherA.ft(self) FatherB.ft(self,0) //显式调用 >>> s =Son() FatherA ft fun! FatherB ft fun!
熟能生巧,来看看这个例子,能输出什么结果?
>>> class FatherA(object): def __init__(self): print(‘FatherA class initing!‘) self.name = ‘FatherA name‘ def get_name(self): return ‘FatherA call ‘+ self.name >>> class FatherB(object): def __init__(self): print(‘FatherB class initing!‘) self.name = ‘FatherB name‘ def get_name(self): return ‘FatherB call ‘+ self.name >>> class Son(FatherA,FatherB): def __init__(self): FatherA.__init__(self) FatherB.__init__(self) print(‘Son class initing!‘) >>> s = Son() >>> s.get_name()
输出结果为:
>>> s = Son() FatherA class initing! FatherB class initing! Son class initing! >>> s.get_name() ‘FatherA call FatherB name‘
解析:
(1)在Son类中,执行__init__函数,调用了FatherA.__init__(self),FatherB.__init__(self),所以self.name 最后为FatherA name。
(2)调用了s.get_name()方法,根据python多重继承规则,从左到右的继承顺序,调用的是FatherA的get_name方法。
继承经典类和新式类
何为经典类/新式类?
答:经典类是python2.2之前的东西,但是在2.7还在兼容,但是在3之后的版本就只承认新式类。新式类在python2.2之后的版本中都可以使用。
经典类/新式类区别?
答:经典类是默认没有派生自某个基类,而新式类是默认派生自object这个基类。
//经典类 class A(): pass //新式类 class A(object): pass
针对于经典类的多重继承采用的是深度优先继承.见例子:
>>> class A(): def f1(self): print(‘A f1‘) >>> class B(A): def f2(self): print(‘B f2‘) >>> class C(A): def f1(self): print(‘C f1‘) >>> class D(B,C): pass >>> d = D() >>> d.f1() A f1
解析:在访问d.f1()的时候,D这个类是没有f1方法。那么往上查找,先找到B,里面也没有,深度优先,访问A,找到了f1(),所以这时候调用的是A的f1(),从而导致C重写的f1()被绕过.
经典类在python3.x彻底被抛弃,在这里就不做过多的介绍。大家记得就好。上面的执行顺序:D->B->A
再来看看新式类:
>>> class A(object): def f1(self): print(‘A-f1‘) >>> class B(object): def f1(self): print(‘B-f1‘) >>> class A(object): def f1(self): print(‘A-f1‘) >>> class B(object): def f1(self): print(‘B-f1‘) def bar(self): print(‘B-bar‘) >>> class C1(A,B): pass >>> class C2(A,B): def bar(self): print ‘C2-bar‘ >>> class D(C1,C2): pass >>> d = D() >>> d.f1() A-f1 >>> d.bar() C2-bar
从上面新式类的输出结果来看,新式类的搜索方式是采用“广度优先”的方式去查找属性。
实例d调用f1()时,搜索顺序是 D -> C1 -> C2 -> A
实例d调用bar()时,搜索顺序是 D -> C1 -> C2
归总python继承的特性:
1.子类如果有构造函数__init__,不会自动调用父类的构造函数。如果子类没有自己的构造函数__init__,则会直接从父类继承构造函数.如果一定需要用到父类的构造函数,则需要在子类的构造函数中显式的调用.
2.子类从多个父类派生,子类没有自己的构造函数时,
(1)按继承顺序,从左到右。第一个父类而它又有自己的构造函数,就继承它的构造函数;
(2)如果最前面第一个父类没有构造函数,则继承第2个的构造函数,如果第2个类也没有,则继承第3个的。以此类推,最后会继承object。
3.新式类通过广度优先的方式查找属性。