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/

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值