1 继承
即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。
子类会继承父类的属性和方法。
1.1 单继承
每个子类只有一个父类的情况
class people:
#定义基本属性
name = ''
age = 0
grade= 0
#定义构造方法
def __init__(self,n,a,g):
self.name = n
self.age = a
self.grade=g
def speak(self):
print("%s 说: 我 %d 岁,读 %d 年级。" %(self.name,self.age,self.grade))
#单继承示例
class student(people):
def __init__(self,n,a,g):
#调用父类的构函
people.__init__(self,n,a,g)
s = student('ken',10,3)
s.speak()
输出结果:
ken 说: 我 10 岁,读 3 年级。
当我们在子类student里也写入一个speak函数时
class people:
#定义基本属性
name = ''
age = 0
grade= 0
#定义构造方法
def __init__(self,n,a,g):
self.name = n
self.age = a
self.grade=g
def speak(self):
print("%s 说: 我 %d 岁,读 %d 年级。" %(self.name,self.age,self.grade))
#单继承示例
class student(people):
def __init__(self,n,a,g):
#调用父类的构函
people.__init__(self,n,a,g)
#覆写父类
def speak(self):
print("%s 说: 我 %d 岁" %(self.name,self.age))
s = student('ken',10,3)
s.speak()
输出结果:
ken 说: 我 10 岁
可以看到当子类和父类中有同名函数时,调用的speak函数会优先在本类中寻找,所以无法输出父类的speak。我们在编写程序的时候最好避免出现同名函数的情况,如果想要输出同名情况下的父类,可以参考下面这篇博客:
子类调用父类同名方法
1.2 多重继承
一个子类含有多个父类继承的情况
1.2.1 不引入_init_构造函数情况
子类C的父类为A和B
class A:
def f_a(self):
print('--------A--------')
class B:
def f_a(self):
print('--------B--------')
class C(A,B):
def f_a(self):
print('--------C--------')
c = C()
c.f_a()
输出结果:
--------C--------
如果我们想在C类中的f_a方法里面使用父类A或者父类B怎么办呢,下面我们采用一种调用父类的方法实现:
class A:
def f_a(self):
print('--------A--------')
class B:
def f_a(self):
print('--------B--------')
class C(A,B):
def f_a(self):
A.f_a(self)
B.f_a(self)
print('--------C--------')
c = C()
c.f_a()
输出结果:
--------A--------
--------B--------
--------C--------
1.2.2 super()机制
super()机制是调用父类常用的方法,它会默认多继承中从左到右的顺序来调用。
在多继承的复杂情况中,为了避免同名的属性被覆盖,python会使用一种叫做C3的算法,来控制类属性的查找顺序,即mro查找。在菱形的继承结构中,会采用广度优先遍历的查找循序;非菱形的继承结构中,则会采用深度优先遍历的查找顺序。
super函数也并不是直接调用父类的方法,它是调用mro顺序里面下一个类的方法。
类名.mro #可以利用类属性 mro 来查看一个类的继承顺序
class A:
def f_a(self):
print('--------A--------')
class B:
def f_a(self):
print('--------B--------')
class C(A,B):
def f_a(self):
super(C,self).f_a()
super(A,self).f_a()
print('--------C--------')
c = C()
c.f_a()
print(C.mro())
输出结果:
--------A--------
--------B--------
--------C--------
[<class ‘main.C’>, <class ‘main.A’>, <class ‘main.B’>, <class ‘object’>]
1.2.3 _init_构造函数
init()函数是一种特殊的方法,被称为类的构造函数或初始化方法,当创建了这个类的实例时就会调用该方法。
self代表类的实例,self在定义类的方法时是必须有的,虽然在调用时不必传入相应的参数。
class A(object):
def __init__(self):
print ('init A...')
class B(A):
def __init__(self):
super().__init__()
print('init B...')
class C(A):
def __init__(self):
super().__init__()
print ('init C...')
class D(B, C):
def __init__(self):
super().__init__()
print ('init D...')
d=D()
print(D.mro())
输出结果:
init A…
init C…
init B…
init D…
[<class ‘main.D’>, <class ‘main.B’>, <class ‘main.C’>, <class ‘main.A’>, <class ‘object’>]
如果你第一次接触多重继承,面对这个结果,你肯定会跟我当初一样的困惑,为什么不是A B C D呢?
我们知道在多重继承中,遇到父类中函数同名的情况下,默认在class D(B,C)中从左到右执行,且只调用类B一次。而在此处执行完类B后调用类A,返回执行的输出B呀。很遗憾,你被这个多重继承给绕进去了,因为此处调用父类采用的super()函数,它拥有特定的索引顺序,如前文所写。
所以我们可以把找上一个父类换成找下一个树的对象,这个树的遍历规则满足mro查找。
D–>B–>C–>A
类D中的第一个super()找类B,类B中的super()找类C,类C中的super()找类A,然后依次输出并返回,故此处的输出’init A…'是类C直接导致的结果。
下面再看一个实例来领悟
class A():
def __init__(self):
print("A")
class B(A):
def __init__(self):
print("B")
super().__init__()
class C(A):
def __init__(self):
print("C")
super().__init__()
class D(B, C):
def __init__(self):
print("D")
super().__init__()
d = D()
print(D.mro())
输出结果:
D
B
C
A
[<class ‘main.D’>, <class ‘main.B’>, <class ‘main.C’>, <class ‘main.A’>, <class ‘object’>]
这是一个菱形的继承结构,如果super()是直接调用父类的方法,那么这里输出 B 的后面应该为 A,而实际这里输出为 C,结论就是super函数和父类没有直接关联,它和MRO查找顺序有关。