• python的super深入了解(转)


    1、python的继承以及调用父类成员

    python子类调用父类成员有2种方法,分别是普通方法和super方法

    假设Base是基类

    class Base(object):
        def __init__(self):
            print("Base init")

    普通方法如下

    class Leaf(Base):
        def __init__(self):
            Base.__init__(self)
            print “Leaf init”

    super方法如下

    class Leaf(Base):
        def __init__(self):
            super(Leaf, self).__init__()
            print “Leaf init”

    在上面的简单场景下,两种方法的效果一致

    >>> leaf = Leaf()
    
    Base init
    
    Leaf init

    2、钻石继承遇到的难题

    如果我们还是用普通方法调用父类成员,代码如下

    class Base(object):
           def __init__(self):
                  print “Base init”
    
    class Medium1(Base):
           def __init__(self):
                  Base.__init__(self)
                  print “Medium1 init”
    
    class Medium2(Base):
           def __init__(self):
                  Base.__init__(self)
                  print “Medium2 init”
    
    class Leaf(Medium1, Medium2):
           def __init__(self):
                  Medium1.__init__(self)
                  Medium2.__init__(self)
                  print “Leaf init”

    当我们实例化Leaf对象时,结果如下

    >>> leaf = Leaf()
    
    Base init
    
    Medium1 init
    
    Base init
    
    Medium2 init
    
    Leaf init

    可以看到Base被初始化了两次,这是由于Medium1和Medium2各自调用了Base的初始化函数导致的

    3、python的解决方法

    python使用的是super来解决这个问题

    我们把上面的钻石继承用super重写一下,看一下输出结果

    class Base(object):
           def __init__(self):
                  print “Base init”
     
    class Medium1(Base):
           def __init__(self):
                  super(Medium1, self).__init__()
                  print “Medium1 init”
    
    class Medium2(Base):
           def __init__(self):
                  super(Medium2, self).__init__()
                  print “Medium2 init”
    
    class Leaf(Medium1, Medium2):
           def __init__(self):
                  super(Leaf, self).__init__()
                  print “Leaf init”

    我们生成Leaf对象:

    >>> leaf = Leaf()
    
    Base init
    
    Medium2 init
    
    Medium1 init
    
    Leaf init

    可以看到整个初始化过程符合我们的预期,Base只被初始化了1次。而且重要的是,相比原来的普通写法,super方法并没有写额外的代码,也没有引入额外的概念

    4、super的内核:mro

    要理解super的原理,就要先了解mro,mro是method resolution order的缩写,表示了类继承体系中的成员解析顺序

    在python中,每个类都有一个mro的类方法,我们来看一下钻石继承中,Leaf类的mro是什么样子的

    >>> Leaf.mro()
    
    [<class '__main__.Leaf'>, <class '__main__.Medium1'>, <class '__main__.Medium2'>, <class '__main__.Base'>, <type 'object'>]

    可以看到mro方法返回的是一个祖先类的列表,Leaf的每个祖先都在其中出现一次,这也是super在父类中查找成员的顺序

    通过mro,python巧妙的将多继承的图结构,转变为list的顺序结构,super在继承体系中向上的查找过程,变成了在mro中向右的线性查找过程,任何类都只会被处理一次

    通过这个方法,python解决了多继承中的2大难题

    1、查找顺序问题,从Leaf的mro顺序可以看出,如果Leaf类通过super来访问父类成员,那么Medium1的成员会在Medium2之前被首先访问到,如果Medium1和Medium2都没有找到,最后再到Base中查找

    2、钻石继承的多次初始化问题,在mro的list中,Base类只出现了一次,事实上任何类都只会在mro list中出现一次,这就确保了super向上调用的过程中,任何祖先类的方法都只会被执行一次

    至于mro的生成算法,可以参考https://en.wikipedia.org/wiki/C3_linearization

    5、super的具体用法

    我们首先来看一下python中的super文档

    >>> help(super)
    
    Help on class super in module __builtin__:
    
    class super(object)
    
     |  super(type, obj) -> bound super object; requires isinstance(obj, type)
    
     |  super(type) -> unbound super object
    
     |  super(type, type2) -> bound super object; requires issubclass(type2, type)

    光从字面来看,这可以算是python中最语焉不详的帮助文档之一了。甚至里面还有一些术语误用。那super究竟应该怎么用呢,我们重点来看super中的第1和第3种用法

    5.1super(type,obj)

    当我们在Leaf的__init__中写这样的super时:

    class Leaf(Medium1, Medium2):
        def __init__(self):
            super(Leaf, self).__init__()
            print “Leaf init”

    super(Leaf,self).__init__()的意思是说:

    1、获取self所属类的mro,也就是[Leaf, Medium1, Medium2, Base]

    2、从mro中Leaf右边的一个类开始,依次寻找__init__函数,这是从Medium1开始寻找

    3、一旦找到,就把找到的__init__函数绑定到self对象,并返回

    从这个执行流程可以看到,如果我们不想调用Medium1的__init__,而想要调用Medium2的__init__,那么

    super应该写成:super(Medium1, self)__init__() 

    5.2super(type,type2)

    当我们在Leaf中写类方法的super时

    class Leaf(Medium1, Medium2):
        def __new__(cls):
              obj = super(Leaf, cls).__new__(cls)
              print “Leaf new”
              return obj

    super(Leaf,cls).__new__(cls)的意思是说

    1、获取cls这个类的mro,这里也是

    2、从mro中Leaf右边的一个类开始,一次寻找__new__函数

    3、一旦找到,就返回“”非绑定”的__new__函数

    由于返回的是非绑定的函数对象,因此调用时不能省略函数的第一个参数,这也是调用__new__时,需要传入参数cls的原因

    同样的,如果我们想从某个mro的某个位置开始查找,只需要修改super的第一个参数就行

     

    6、小结

    至此,我们讲解了和super相关的用法及原理,小结一下我们讲过的内容有:

    1、python调用父类成员共有2种方法:普通方法,super方法

    2、在钻石继承中,普通方法会遇到Base类两次初始化的问题

    4、用实例展示了python使用super可以解决此问题

    5、讲解了super两种主要的用法及原理

    6、Python3.x 和 Python2.x 的一个区别是: Python 3 可以使用直接使用 super().xxx 代替 super(Class, self).xxx :

    参考https://www.cnblogs.com/testview/p/4651198.html

  • 相关阅读:
    SharePoint 2013 配置基于表单的身份认证
    SharePoint 2013 场解决方案包含第三方程序集
    SharePoint 2010 站点附加数据升级到SP2013
    SharePoint 2013 在母版页中插入WebPart
    SharePoint 2013 搭建负载均衡(NLB)
    SharePoint 部署解决方案Feature ID冲突
    SharePoint 2013 配置基于AD的Form认证
    SharePoint Server 2016 Update
    SharePoint 2013 为用户组自定义EventReceiver
    SharePoint 2013 JavaScript API 记录
  • 原文地址:https://www.cnblogs.com/z-x-y/p/11178502.html
Copyright © 2020-2023  润新知