本节内容
1.python多继承
2.python经典类的MRO
3.python新式类的MRO,C3算法
4.super()是什么
一、python多继承
·当出现了x是⼀种y的的时候. 就可以使⽤继承关系. 即"is-a" 关系.
·在继承关系中. ⼦类⾃动拥有⽗类中除了私有属性外的其他所有内容. python⽀持多继承.
·⼀个类可以拥有多个⽗类.
·一个父类也可以有多个子类
1、两种类
经典类(python2,仅在面试会有)
·在python2.2之前. 已经是历史了.
·MRO 采用的是树形结构的深度遍历(一条道跑到黑)
·经典类在基类的根如果什么都不写. 表⽰继承xxx.
新式类(python3,现在所使用的)
·在2.2之后产生新式类. 目前我们使用的. 所有的类的根都是object C3算法(merge)
·如果基类谁都不继承. 那这个类会默认继承object
fe:新式类的示例
class Shen:
def fly(self):
print("大神会飞")
class Hou:
def chi(self):
print("猴子吃桃子")
class SunWuKong(Shen, Hou): # 一个类可以继承多个无关的类. 一个类可以被多个无关的类继承
pass
class TaiShangLaoJun(Shen):
pass
#
# swk = SunWuKong()
# swk.fly()
# swk.chi()
二、经典类的MRO
学一学,这是一种树形结构遍历的一个最直接案例。
fe:已经没有机器跑2.0了,只能说明原理,手动求解
class A:
pass
class B(A):
pass
class C(A):
pass
class D(B, C):
pass
class E:
pass
class F(D, E):
pass
class G(F, D):
pass
class H:
pass
class Foo(H, G):
pass
类的MRO: Foo-> H -> G -> F -> D -> B -> A -> C -> E
1、深度优先和广度优先
如图. 肯定是按照123456这样的顺序来送. 那这样的顺序就叫深度优先遍历.
⽽如果是142356呢? 这种被称为⼴度优先遍历.
好了. 深度优先就说这么多. 那么上⾯那个图怎么呢?
MRO是什么呢?
很简单. 记住. 从头开始. 从左往右. ⼀条路跑到头, 然后回头. 继续⼀条路跑到头.
就是经典类的MRO算法.
三、新式类的MRO(要会算,给面试官用的,实战上会调用就行)
python中的新式类的MRO是采⽤的C3算法来完成的.
fe:C3算法
class A:
pass
class B(A):
pass
class C(A):
pass
class D(B, C):
pass
class E(C, A):
pass
class F(D, E):
pass
class M(F, E):
pass
class N:
pass
class P(M,N):
pass
class G(P):
pass
class O:
pass
class X(O):
pass
class H(G, X, F):
pass
print(H.__mro__)
'''
# 计算过程
L(H) = H + L(G) + L(X) + L(F) + GXF HGPMXFDBECANO
L(G) = G + L(P) + P # GPMFDBECAN
L(X) = X + L(O) + O # XO
L(F) = F + L(D) + L(E) + DE # FDBECA
L(P) = P + L(M) + L(N) + MN # PMFDBECAN
L(D) = D + L(B) + L(C) + BC # DBCA
L(E) = E + L(C) + L(A) + CA # ECA
L(M) = M + L(F) + L(E) + FE # ECA ECA E MFDBECA
'''
1、C3算法的关键:merge原则
最后就剩下⼀个A了. 也就不⽤再找了. 接下来. 把L(A) 往⾥带. 再推回去.
但要记住. 这⾥的+ 表⽰的是merge.
*merge的原则:是⽤每个元组的头⼀项和后⾯所有元组的除头⼀项外的其他元素进⾏比较, *
1.看是否存在. 如果存在. 就从下⼀个元组的头⼀项继续找. 下一组找到之后返回头一项,
2.如果找不到. 就拿出来.作为merge的结果的⼀项. 以此类推. 直到元组之间的元素都相同. 也就不⽤再找了.
2、为什么要会算,因为面试
结果OK. 那既然python提供了. 为什么我们还要如此⿇烦的计算MRO呢?
因为笔试.......你在笔试的时候, 是没有电脑的. 所以这个算法要知道.
并且简单的计算要会. 真是项⽬开发的时候很少有⼈这么去写代码.
四、super是什么
super()可以帮我们执⾏MRO中下⼀个⽗类的⽅法. 通常super()有两个使⽤的地⽅:
1. 可以访问⽗类的构造⽅法
2. 当⼦类⽅法想调⽤⽗类(MRO)中的⽅法
fe1: 访问父类的构造方法
# super是查找mro顺序中的下一个
# 单继承中我们可以认为super是对父类中的属性或方法的引入
class ShengWu:
def dong(self): # 实例方法
print(self)
print("我是生物")
class Animal(ShengWu):
pass
class Cat(Animal):
def dong(self): # 子类中出现了和父类重名的内容. 表示对父类的方法的覆盖(重写). 半盖(java)
super(Animal, self).dong() # 定位到Animal. 找Animal的下一个
# super().dong() # 默认调用上一个父类的,常用
# super(类, 对象).方法() 找到MRO中的类. 找这个类的下一个. 去执行方法
print("我的猫也会动")
# 找MRO中的下一个
# Cat -> Animal -> ShengWu
c = Cat()
print(c)
c.dong()
fe2: ⼦类⽅法想调⽤⽗类(MRO)中的⽅法
class Foo:
def func1(self):
super().func1() # 此时找的是MRO顺序中下⼀个类的func1()⽅法
print("我的⽼家. 就住在这个屯")
class Bar:
def func1(self):
print("你的⽼家. 不在这个屯")
class Ku(Bar, FOO):
def func1(self):
super().func1() # 此时super找的是Foo
print("他的⽼家. 不知道在哪个屯")
k = Ku() # 先看MRO . KU, FOO, BAR object
k.func1()
k2 = Foo() # 此时的MRO. Foo object
k2.func1() # 报错
面试题:MRO + super
# MRO + super ⾯试题
class Init(object):
def __init__(self, v):
print("init")
self.val = v # 2
class Add2(Init):
def __init__(self, val): # 2
print("Add2")
super(Add2, self).__init__(val)
print(self.val) # 5.0
self.val += 2 # 7.0
class Mult(Init):
def __init__(self, val):
print("Mult")
super(Mult, self).__init__(val)
self.val *= 5 # 5.0
class HaHa(Init):
def __init__(self, val):
print("哈哈")
super(HaHa, self).__init__(val)
self.val /= 5 # 1.0
class Pro(Add2,Mult,HaHa): #
pass
class Incr(Pro):
def __init__(self, val): # 5
super(Incr, self).__init__(val)
self.val += 1 # 8.0
# Incr, pro, add2, mult, haha, Init
p = Incr(5)
print(p.val) # ?
# Add2 init
c = Add2(2)
print(c.val) # ?