代码之家  ›  专栏  ›  技术社区  ›  Mark

Python2__bases__和超级

  •  1
  • Mark  · 技术社区  · 12 年前

    在Python 2.7中,我试图从某个类重构AN继承链 E 到根 A 。存在如下所示的菱形继承问题,但我对路径感兴趣,而不是路径,因此它应该可以工作。这是否是我应该做的事情(以这种方式)是值得怀疑的,但现在我只想知道我误解了什么。。。

    class A(object):
        @classmethod
        def supah(thisCls):
            return [cls for cls in thisCls.__bases__ if issubclass(cls, A)]
        def name(self):
            return 'A'
    
    class C(A):
        def name(self):
            return 'C'
    
    class D(A):
        def name(self):
            return 'D'
    
    class E(C, D):
        def name(self):
            return 'E'
    
    current = E
    while True:
        try:
            print current, super(current, E()), super(current, E()).name()
        except AttributeError:
            break
        current = current.supah()[0]
    

    输出

    <class '__main__.E'> <super: <class 'E'>, <E object>> C
    <class '__main__.C'> <super: <class 'C'>, <E object>> D
    <class '__main__.A'> <super: <class 'A'>, <E object>>
    

    D在那里做什么?它正在呼叫

    super(C, E()).name()
    

    哪里 super(C, E()) 应该是“A级”,对吧?如果第一行的C是D,我会理解,但在我看来,第二行肯定是A。

    有什么帮助吗?

    编辑 我的理解是

    super(C, obj).name()
    

    将产生名称“A”,因为C的线性化是 [C, A, object] .

    然而,这不是什么 super(C, obj).name() 意思很明显。它仍然使用obj的完全线性化: [E, C, D, A, object] (感谢@Martijn Pieters),它只在C之后开始。因此D在A之前。

    2 回复  |  直到 12 年前
        1
  •  3
  •   Martijn Pieters    12 年前

    super() 不看 __bases__ ; 它查看方法解决顺序(MRO),通过 type(self).mro() :

    >>> E.mro()
    [<class '__main__.E'>, <class '__main__.C'>, <class '__main__.D'>, <class '__main__.A'>, <type 'object'>]
    

    正如你所看到的, D 因为它是 E ; 当你打电话时 super(C, E()).name() , D 在MRO中排名第二。

    MRO将始终包括 全部的 层次结构中的基类;无法在无法建立MRO的地方构建类层次结构。这可以防止在菱形继承模式中跳过类。

    MRO的工作原理详见 The Python 2.3 Method Resolution Order .

    您可能还想阅读 Guido van Rossum's explanation ; 他使用了一个菱形图案:

    class A:
      def save(self): pass
    
    class B(A): pass
    
    class C(A):
      def save(self): pass
    
    class D(B, C): pass
    

    说明MRO为何重要;呼叫时 D().save() 你会想要的 C.save() 要被调用(更专门), A.save() .

    如果你真的想 跳过 D 从…起 C.name ,您必须明确查找 C.__bases__[0] 在MRO中,然后告诉 super() 开始搜索下一个 .name() 方法:

    mro = type(self).mro()
    preceding = mro[0]
    for i, cls in enumerate(mro[1:], 1):
        if cls in self.__bases__:
            preceding = mro[i - 1]
    name = super(preceding, self).name()
    

    对于您的 E.mro() 和类 C ,这会发现 D ,因为它位于的第一个基类之前 C , A 使命感 super(D, self).name() 然后告诉 super() 找到第一节课 D 用一个 name() 方法,即 A. 在这里

        2
  •  0
  •   Community Mohan Dere    9 年前

    @Martijn Pieters的回答解释了观察到的结果是如何产生的。

    如果有人想产生我对super错误期望的结果,可以使用基于@Sven Marnach在 python: super()-like proxy object that starts the MRO search at a specified class

    如果你想得到一个班级应有的东西 A 的版本 C 实例:

    class Delegate:
        def __init__(self, cls, obj):
            self._delegate_cls = cls
            self._delegate_obj = obj
        def __getattr__(self, name):
            x = getattr(self._delegate_cls, name)
            if hasattr(x, "__get__"):
                return x.__get__(self._delegate_obj)
            return x
    

    可以用来获取 .name() 从…起 A. 这样地:

    class C(A):
        def name(self):
            return delegate(A, self).name() + 'C'
    C().name()
    # result: AC
    

    如果您对获得(第一个)直系祖先的类超构造感兴趣:

    class parent:
        def __init__(self, cls, obj):
            if len(cls.__bases__):
                self._delegate_cls = cls.__bases__[0]
            else:
                raise Exception('parent called for class "%s", which has no base classes')
            self._delegate_obj = obj
        def __getattr__(self, name):
            x = getattr(self._delegate_cls, name)
            if hasattr(x, '__get__'):
                return x.__get__(self._delegate_obj)
            return x
    

    这样称呼:

    class C(A):
        def name(self):
            return parent(C, self).name() + 'C'
    print C().name()
    # result: AC
    

    我认为没有办法不显式包含当前类的名称,就像 super (在py2中)。

    请注意,这适用于特殊情况。例如,在我的示例中,如果 C 无法实现 .name() 它会打开 A. 从不 D 。但是,它确实允许您从一个类到根的直接祖先行(而不是“the”)。而且 parent(Cls, obj) 将始终是 obj ,不是一个 Cls 对它一无所知,但它恰好是 物镜 .