• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
66zhyh99
博客园    首页    新随笔    联系   管理    订阅  订阅

笔记4:由super()学习python的继承机制以及C3算法

**由super()学习python的继承机制以及C3算法**

python super():

__init__() 是 Python 的构造方法。

举个例子:

 
 
 
 
 
 
 
class Bird:
    def __init__(self):
          self.hungry = True
    def eat(self):
          if self.hungry:
               print 'Ahahahah'
          else:
               print 'No thanks!'
​
class SongBird(Bird):
     def __init__(self):
          self.sound = 'Squawk'
     def sing(self):
          print self.sound
​
sb = SongBird()
sb.sing()    # 能正常输出
sb.eat()     # 报错,因为 songgird 中没有 hungry 特性
 

在这个代码上,由于父类没有构造,导致sb.eat() 不能调用。

解决这个就要用以下两种方法:

 
 
 
 
 
 
 
def __init__(self):
    Bird.__init__(self)
    self.sound = 'Squawk'
#原理:在调用了一个实例的方法时,该方法的self参数会自动绑定到实例上(称为绑定方法);如果直接调用类的方法(比如Bird.__init__),那么就没有实例会被绑定,可以自由提供需要的self参数(未绑定方法)。
#或者
def __init__(self):
    super(SongBird,self).__init__()
    self.sound = 'Squawk'
#它会查找所有的超类,以及超类的超类,直到找到所需的特性为止。
 

现在B类继承A类,我们要给B类一个独特的初始属性,不能写__init__(self),因为这样写就相当于重写父类的。super(B,self).__init__( )

 
 
 
 
 
 
 
class A():
    def __init__(self):
        self.first_name = 'king'
    def second_name(self):
        print('hao')

class B(A):
    def __init__(self):
        super(B,self).__init__( )#同时也可以写成B.__init__(self),此处可以传参数
        self.english_name = 'horsun'
    def full_name(self):
        print('king hao')
 

注意:class后面的继承顺序init里面优先,其次才是class里面的顺序有关,

注意:当一个类继承2个单独的类,而这两个类又继承自同一个类,,此时,会多次调用顶端类的"__init__()"。

 
 
 
 
 
 
 
class MyBaseClass:
    def __init__(self, value):
        self.value = value
​
​
class TimesFive(MyBaseClass):
    def __init__(self, value):
        MyBaseClass.__init__(self, value)
        self.value *= 5
​
​
class PlusTwo(MyBaseClass):
    def __init__(self, value):
        MyBaseClass.__init__(self, value)
        self.value += 2
​
​
class ThisWay(TimesFive, PlusTwo):
    def __init__(self, value):
        TimesFive.__init__(self, value)
        PlusTwo.__init__(self, value)
​
​
​
foo = ThisWay(5)
print('Should be (5*5)+2 but is ', foo.value)#7
 

解决上面的问题用如下

 
 
 
 
 
 
 
class MyBaseClass:
    def __init__(self, value):
        self.value = value
​
​
class TimesFive(MyBaseClass):
    def __init__(self, value):
        super(TimesFive, self).__init__(value)
        self.value *= 5
​
​
class PlusTwo(MyBaseClass):
    def __init__(self, value):
        super(PlusTwo, self).__init__(value)
        self.value += 2
​
​
class GoodWay(TimesFive, PlusTwo):
    def __init__(self, value):
        super(GoodWay, self).__init__(value)
​
​
foo = GoodWay(5)
print('Should be 5*(5+2) and is ', foo.value)#35
print(GoodWay.mro())#[<class '__main__.GoodWay'>, <class '__main__.TimesFive'>, <class '__main__.PlusTwo'>, <class '__main__.MyBaseClass'>, <class 'object'>]
 

python继承算法,最常用MRO C3算法:

最开始Python使用经典类的MRO(深度优先)b->[a,c]->d,搜索顺序为B,A,D,C导致C中的最后找,不合理。

后来新式类的MRO(广度优先)(判断好节点,即N之后的节点都不继承自N,N就是好节点)B,A,D,C,D,但是发现D不是好节点,去掉变为B,A,C,D

C3算法解决了单调性问题和只能继承无法重写问题。

单调性:不符合顺序,即新式中,明明B,A,D,但是却不按这个顺序

继承无法重写:本质是新式中先访问了子类的父类:使用拓扑排序

对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。

且该序列必须满足下面两个条件:

  • 每个顶点出现且只出现一次。

  • 若存在一条从顶点A到顶点B的路径,那么在序列中顶点A出现在顶点B的前面。

    img

    {1,2,4,3,5}

     

C3我感觉是拓扑算法的反向。

C3算法实现了三种重要特性:

  • 保持继承拓扑图的一致性。

  • 保证局部优先原则(比如A继承C,C继承B,那么A读取父类方法,应该优先使用C的方法而不是B的方法)。

  • 保证单调性原则(即子类不改变父类的方法搜索顺序)。

    具体如下

    1.

 
 
 
 
 
 
 
class B(A)
 

这时B的mro序列为[B,A]

2.

 
 
 
 
 
 
 
class B(A1,A2,...,An)
 

这时B的mro序列:

 
 
 
 
 
 
 
mro(B) = [B] + merge(mro(A1), mro(A2),...,mro(An), [A1,A2,...,An])
 

merge操作就是C3算法的核心,是递归运算。

遍历执行merge操作的序列,如果一个序列的第一个元素,在其他序列如果出现也是第一个元素,则从所有执行merge操作序列中删除这个元素,合并到当前的mro中。merge操作后的序列,递归地执行merge操作,直到merge操作的序列为空。

如果merge操作的序列无法为空,则说明不合法。

 

看人话:

class A(object):pass
class B(object):pass
class C(object):pass
class E(A,B):pass
class F(B,C):pass
class G(E,F):pass

A、B、C都继承至一个基类,所以mro序列依次为[A,O]、[B,O]、[C,O]

 

执行merge操作的序列为[A,O]、[B,O]、[A,B] A是序列[A,O]中的第一个元素,在序列[B,O]中不出现,在序列[A,B]中也是第一个元素,所以从执行merge操作的序列([A,O]、[B,O]、[A,B])中删除A,合并到当前mro,[E]中。

 

接着B也满足,

 

cls.mro()就会打印MRO List。

举个例子

class A:
    def method(self):
      print("CommonA")
class B(A):
    pass
class C(A):
    def method(self):
      print("CommonC")
class D(B, C):
    pass
print(D().method())

返回

CommonC

由此我们可以进一步学习python多重继承到底是继承谁

 

posted @ 2022-03-11 19:37  66zhyh99  阅读(46)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3