• Python深入:super函数


            新式类中最酷的,或者也是最不平常的特性之一,可能就是编写“cooperative类”。‘cooperative类’通过多继承,使用我称之为‘cooperative super call’的模式。

            先来看一下传统的,非cooperative super call的特点:当类C继承了基类B时,C希望覆盖定义在B中的一个方法m,当在C中定义的方法m中,调用了B中定义的m方法时,一个super call就产生了。比如:

    class B:
       def m(self):
           print "B here"
    
    class C(B):
       def m(self):
           print "C here"
           B.m(self)

            这时,我们称C的方法m扩展了B的方法m。这种模式在单继承中工作的很好,但是在多继承中就未必了。我们来看一下下面的4个类,他们的继承关系形成了一个菱形图。

    class A(object): ..
    
    class B(A): ...
    
    class C(A): ...
    
    class D(B, C): ...

            假设A定义了方法m,然后B和C都扩展了它。那么D怎么办呢?它继承了m的两个实现,一个来自B,一个来自C。传统上,python简单的使用它最早发现的那个。这种情况下,B中的m就是第一个被检索到的。

            但是这并不理想,因为这完全忽视了C中的定义。看一下忽视C中m的错误在哪,假设这些类代表了某种类型的持续性容器。并且,考虑一个方法实现了‘将数据存储到磁盘’的操作。大概情况下,D的实例拥有B和C的数据,以及A的数据。那忽略C中该方法的定义,意味着D的实例仅仅会保存A和B的数据,而不会保存C中定义的数据。

            在C++中,D继承了两个相互冲突的m的定义,那会引发一个错误信息。然后D的作者就会重写m来避免这种错误。但是D中的m会做什么呢?它会调用C的m,然后是B的m。但是它们又都继承自A。所以A的m会被调用两次。经典的python会有同样的问题,除了它根本不认为继承两个冲突定义的方法是错误的:它简单的选择第一个。

            传统的解决方法是:将m分为两部分,一部分是_m,它仅保存自己的数据,另一部分是m,它会调用自己的_m以及父类的_m们。比如:

    class A(object):
       def m(self): "save A's data"
    
    class B(A):
       def _m(self): "save B's data"
       def m(self):  self._m(); A.m(self)
    
    class C(A):
       def _m(self): "save C's data"
       def m(self):  self._m(); A.m(self)
    
    class D(B, C):
       def _m(self): "save D's data"
       def m(self):  self._m();B._m(self); C._m(self); A.m(self)

            这种模式有多个问题,首先是冗余的方法和定义,但是更重要的是:它引起了继承类对父类不必要的依赖:A的存在不仅是B和C要考虑的实现细节,甚至D也需要知道。如果未来我们需要移除A和B,C之间依赖关系,这也会影响到D。类似的,如果我们为B和C增加一个父类AA,那么B和C的子类也必须知道这一点。

            ‘call-next-method’的模式,结合新的MRO,很好的解决了这个问题,如下:

    class A(object):
       def m(self): "save A's data"
    
    class B(A):
       def m(self): "save B's data"; super(B, self).m()
    
    class C(A):
       def m(self): "save C's data"; super(C, self).m()
    
    class D(B, C):
       def m(self): "save D's data"; super(D, self).m()

     

            注意:这里super的第一个参数永远是调用它的类;第二个参数总是self。现在,为了解释super是如何工作的,考虑每个类的MRO,可以查看每个类的__mro__属性:

    A.__mro__ == (A, object)
    
    B.__mro__ == (B, A, object)
    
    C.__mro__ == (C, A, object)
    
    D.__mro__ == (D, B, C, A, object)

            表达式super(C,self).m应该仅用在类C的m的实现中。要牢记在心的是:self是C的实例,但是self.__class__不一定就是C;他可能是C的子类,比如D。

            表达式super(C,self).m会查询self.__class__.__mro__中,C的位置,然后,在这个位置之后,开始寻找m方法的实现。比如,如果self是C的实例,那么super(C,self).m将会找到A中的m,同样的,super(B,self).m中,如果self是B的实例,也会有同样的效果。

            但是考虑D的实例,在D中的m方法中,super(D,self).m将会搜索,然后找到了B.m(self),因为B是在D.__mro__中紧跟在D之后的,第一个实现了m的类。然后,在B.m中,调用了super(B,self).m()。因为self是D的实例,而MRO是(D,B,C,A,object),并且,跟在B之后的是C,所以就会调用C.m。在C.m中,调用super(C, self).m(),依然是寻找相同的MRO,可以看到跟在C后的是A,因此A.m被调用了,而这就是终点了。

            要注意的是:同样的super表达式,却找到了不同的实现了m的类,这取决于self。这是cooperativesuper 机制的关键所在。

            下面是一个super内嵌类的完整实现:

    class Super(object):
       def __init__(self, type, obj=None):
           self.__type__ = type
           self.__obj__ = obj
    
       def __get__(self, obj, type=None):
           if self.__obj__ is None and obj is not None:
                return Super(self.__type__, obj)
           else:
                return self
    
       def __getattr__(self, attr):
           if isinstance(self.__obj__, self.__type__):
                starttype = self.__obj__.__class__
           else:
                starttype = self.__obj__
    
           mro = iter(starttype.__mro__)
    
           for cls in mro:
                if cls is self.__type__:
                    break
    
           # Note: mro is an iterator, so the second loop
           # picks up where the first one left off!
           for cls in mro:
                if attr in cls.__dict__:
                    x = cls.__dict__[attr]
                    if hasattr(x,"__get__"):
                        x = x.__get__(self.__obj__)
                    return x
    
           raise AttributeError, attr
    
     
    class A(object):
       def m(self):
           return "A"
    
    
    class B(A):
       def m(self):
           return "B" + Super(B, self).m()
    
    
    class C(A):
       def m(self):
           return "C" + Super(C, self).m()
    
     
    class D(C, B):
       def m(self):
           return "D" + Super(D, self).m()
    
    
    print D().m() # "DCBA"


    https://www.python.org/download/releases/2.2.3/descrintro/

     

  • 相关阅读:
    [CSP-S模拟测试]:F(DP+线段树)
    [CSP-S模拟测试]:E(贪心)
    [CSP-S模拟测试]:D(暴力+剪枝)
    [CSP-S模拟测试]:C(三分+贪心)
    [CSP-S模拟测试]:B(DP+数学)
    [CSP-S模拟测试]:A(数学)
    [CSP-S模拟测试]:密码(数位DP+库默尔定理)
    BZOJ3456 城市规划 【多项式求逆】
    多项式求逆
    uoj185 [ZJOI2016]小星星 【dp + 容斥】
  • 原文地址:https://www.cnblogs.com/gqtcgq/p/7247160.html
Copyright © 2020-2023  润新知