6

我想知道从 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__()调用正确的构造函数。

4

2 回答 2

4

你不能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类是如何继承自DeriveandMixin的,因此 MRO 中的下一个类被发现是Mixin不是 Base,因为Mixin 派生自Base

这复制了什么super();在 MRO 中为实例找到下一个类,相对于当前类。


*有关背景,请参阅Python 3.x 的 super() 为何具有魔力?

于 2015-08-14T16:07:17.830 回答
2

从您的评论中,您想知道如何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 线性化的维基百科条目的示例声明一个类层次结构,对于一个足够复杂的示例,请注意元类repr在 Python3 中不起作用,但属性分配不会破坏它。另请注意,我们使用了与 Python 3super(Name, self)中等效的完整 super 调用,并且仍然可以使用: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'>
于 2015-08-14T16:10:32.353 回答