【问题标题】:How do I get the instance method's next-in-line parent class from `super()` in Pythonpython - 如何从Python中的`super()`获取实例方法的下一个父类
【发布时间】:2015-08-14 16:04:45
【问题描述】:

我想知道从 super() 函数获得的实​​例的类型。我试过print(super())__print(type(super()))__

class Base:
    def __init__(self):
        pass

class Derive(Base):
    def __init__(self):
        print(super())        
        print(type(super()))        
        super().__init__()

d = Derive()

结果是

<super: <class 'Derive'>, <Derive object>>
<class 'super'>

有了这些结果,我想知道super().__init__() 如何调用正确的构造函数。

【问题讨论】:

  • @MarcusMüllerꕺꕺ 我认识先生。我很抱歉不清楚。我更改了标题并编辑了我的问题以更具体地说明我的需要。
  • @MarcusMüllerꕺꕺ:type(self).__mro__ 会因子类而异。 __class__.__mro__ 将捕获电流。
  • @AaronHall 我再次编辑了标题和问题。很抱歉误导了基类名称。

标签: python super method-resolution-order


【解决方案1】:

你不能直接用super() 做你想做的事。请转至 MRO 课程(请参阅 class.__mro__):

class Derive(Base):
    def __init__(self):
        mro = type(self).__mro__
        parent = mro[mro.index(__class__) + 1]
        print(parent)

这里的__class__ 是魔术闭包变量*,它引用了定义当前函数的类;即使您使用Derive 子类化或混合其他类,上述内容仍然有效,即使您生成了菱形继承模式。

演示:

>>> class Base: pass
... 
>>> class Derive(Base):
...     def __init__(self):
...         mro = type(self).__mro__
...         parent = mro[mro.index(__class__) + 1]
...         print(parent)
... 
>>> Derive()
<class '__main__.Base'>
<__main__.Derive object at 0x10f7476a0>
>>> class Mixin(Base): pass
... 
>>> class Multiple(Derive, Mixin): pass
... 
>>> Multiple()
<class '__main__.Mixin'>
<__main__.Multiple object at 0x10f747ba8>

注意Multiple 类是如何从DeriveMixin 继承的,因此发现MRO 中的下一个类是Mixin不是 Base,因为Mixin 派生自Base

这复制了super() 所做的事情;在 MRO 中为实例找到下一个类,相对于当前类。


* 背景见Why is Python 3.x's super() magic?

【讨论】:

    【解决方案2】:

    从您的 cmets 中,您想知道 super 如何知道接下来要调用哪个方法。 Super 检查实例的 mro,知道它所在的当前类方法,并调用下一个。以下演示将在 Python 2 和 3 中运行,在 Python 2 中,由于元类,它会打印每个类的名称,因此我将使用该输出:

    首先导入和设置以使打印更好:

    import inspect
    
    class Meta(type):
        def __repr__(cls):
            return cls.__name__
    

    接下来,我们定义一个函数,根据超级对象本身告诉我们发生了什么

    def next_in_line(supobj):
        print('The instance class: {}'.format(supobj.__self_class__))
        print('in this class\'s method: {}'.format(supobj.__thisclass__))
        mro = inspect.getmro(supobj.__self_class__)
        nextindex = mro.index(supobj.__thisclass__) + 1
        print('super will go to {} next'.format(mro[nextindex]))
    

    最后,我们根据C3 linearization 上的维基百科条目中的示例声明一个类层次结构,对于一个足够复杂的示例,请注意元类repr 在 Python3 中不起作用,但属性分配不会中断它。另请注意,我们使用 super(Name, self) 的完整超级调用,这相当于 Python 3 中的 super(),并且仍然可以工作:

    class O(object):
        __metaclass__ = Meta
    
        def __init__(self):
            next_in_line(super(O, self))
            super(O, self).__init__()
    
    class A(O):
        def __init__(self):
            next_in_line(super(A, self))
            super(A, self).__init__()
    
    
    class B(O):
        def __init__(self):
            next_in_line(super(B, self))
            super(B, self).__init__()
    
    
    class C(O):
        def __init__(self):
            next_in_line(super(C, self))
            super(C, self).__init__()
    
    
    class D(O):
        def __init__(self):
            next_in_line(super(D, self))
            super(D, self).__init__()
    
    
    class E(O):
        def __init__(self):
            next_in_line(super(E, self))
            super(E, self).__init__()
    
    
    class K1(A, B, C):
        def __init__(self):
            next_in_line(super(K1, self))
            super(K1, self).__init__()
    
    
    class K2(D, B, E):
        def __init__(self):
            next_in_line(super(K2, self))
            super(K2, self).__init__()
    
    
    class K3(D, A):
        def __init__(self):
            next_in_line(super(K3, self))
            super(K3, self).__init__()
    
    
    class Z(K1, K2, K3):
        def __init__(self):
            next_in_line(super(Z, self))
            super(Z, self).__init__()
    

    现在当我们打印 Z 的 mro 时,我们得到了这个算法定义的方法解析顺序应用于继承树:

    >>> print(inspect.getmro(Z))
    (Z, K1, K2, K3, D, A, B, C, E, O, <type 'object'>)
    

    而当我们调用Z()时,因为我们的函数使用了mro,所以我们会依次访问每个方法:

    >>> Z()
    The instance class: Z
    in this class's method: Z
    super will go to K1 next
    The instance class: Z
    in this class's method: K1
    super will go to K2 next
    The instance class: Z
    in this class's method: K2
    super will go to K3 next
    The instance class: Z
    in this class's method: K3
    super will go to D next
    The instance class: Z
    in this class's method: D
    super will go to A next
    The instance class: Z
    in this class's method: A
    super will go to B next
    The instance class: Z
    in this class's method: B
    super will go to C next
    The instance class: Z
    in this class's method: C
    super will go to E next
    The instance class: Z
    in this class's method: E
    super will go to O next
    The instance class: Z
    in this class's method: O
    super will go to <type 'object'> next
    

    我们停在object.__init__。从上面我们可以看到super总是知道它在哪个实例的类,它当前所在的类的方法,并且可以从实例类的MRO中推断出下一步要去哪里。


    我想知道基类的名称?

    如果你只想要直接基(或多个,在多重继承的情况下),你可以使用__bases__属性,它返回一个元组

    >>> Derive.__bases__
    (<class __main__.Base at 0xffeb517c>,)
    
    >>> Derive.__bases__[0].__name__
    'Base'
    

    我建议使用检查模块来获取方法解析顺序(super 基于原始调用者的类):

    >>> import inspect
    >>> inspect.getmro(Derive)
    (<class __main__.Derive at 0xffeb51dc>, <class __main__.Base at 0xffeb517c>)
    

    从超级市场获得它

    super().__self_class__ 给出实例类,super().__thisclass__ 给出当前类。我们可以使用实例的 MRO 并查找下一个类。我认为您不会在最终父级中执行此操作,因此我没有发现索引错误:

    class Base:
        def __init__(self):
            print(super().__self_class__)
            print(super().__thisclass__)
    
    
    class Derive(Base):
        def __init__(self):
            print(super().__self_class__)
            print(super().__thisclass__)
            mro = inspect.getmro(super().__self_class__)
            nextindex = mro.index(super().__thisclass__) + 1
            print('super will go to {} next'.format(mro[nextindex]))
            super().__init__()
    
    
    >>> d = Derive()
    <class '__main__.Derive'>
    <class '__main__.Derive'>
    super will go to <class '__main__.Base'> next
    <class '__main__.Derive'>
    <class '__main__.Base'>
    

    【讨论】:

    • 请注意,super() 在多继承场景中可以横向
    • 他们可能对super() 和 MRO 的工作方式困惑,当涉及到菱形模式的多重继承时。
    • 如果是这种情况,那就是 XY 问题。我在所陈述的问题上添加了问号,并直接回答了它。基于这个问题,我也投票关闭。
    • @AaronHall 谢谢你的回答。然而,正如 MartijnPieters 在他的回答中所说,我不能直接用 super() 来做。而且您的解决方案还需要另一个模块与 super() 一起使用。无论如何,我想我不明白为什么 super().__init__() 调用正确的构造函数,甚至 super() 类似于 , > 和 type(super( )) 是 .
    • @wannik 好的,用更多信息更新我的答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-10
    • 2021-05-24
    • 1970-01-01
    • 2020-05-30
    • 1970-01-01
    相关资源
    最近更新 更多