当type是'object'时'super()'的行为?

时间:2018-06-02 12:02:16

标签: python super

Python website上找到的关于super()的文档说它返回一个代理对象,该方法将方法调用委托给父类或兄弟类。在super considered superhow does super() work with multiple inheritance super considered harmful找到的信息说明实际上使用了mro中的下一个方法。我的问题是,如果使用super(object, self).some_method()会发生什么?由于object通常出现在mro列表的末尾,我猜搜索会立即以异常结束。但事实上,似乎代理本身的方法被调用,如super(object, self).__repr__()所示,显示了超级对象本身。我想知道super()object的行为是否不能完全委托方法。 如果是这种情况,我想知道任何可靠的材料都会提到它,以及它是否适用于其他Python实现。

class X(object):
    def __init__(self):
        # This shows [X, object].
        print X.mro()

        # This shows a bunch of attributes that a super object can have.
        print dir(super(object, self))

        # This shows something similar to <super object at xxx>
        print(object, self)

        # This failed with `super() takes at least one argument`
        try:
            super(object, self).__init__()
        except:
            pass

        # This shows something like <super <class 'object'>, <'X' object>>.
        print(super(object, self).__repr__())

        # This shows the repr() of object, like <'X' object at xxx>
        print(super(X, self).__repr__())


if __name__ == '__main__':
    X()

2 个答案:

答案 0 :(得分:2)

如果super在查看方法解析顺序(MRO)以查找(或者如果您正在查找属性__class__)时找不到某些内容,则会检查自己的属性。

因为object始终是MRO中的最后一个类型(至少据我所知,它始终是最后一个),所以你有效地禁用了委托,它将检查超级实例。

我发现这个问题非常有趣所以我去了super的源代码,特别是代理部分(super.__getattribute__ (in CPython 3.6.5)),我把它(粗略地)翻译成了纯Python并伴随着一些额外的评论我个人:

class MySuper(object):
    def __init__(self, klass, instance):
        self.__thisclass__ = klass
        self.__self__ = instance
        self.__self_class__ = type(instance)

    def __repr__(self):
        # That's not in the original implementation, it's here for fun
        return 'hoho'

    def __getattribute__(self, name):
        su_type = object.__getattribute__(self, '__thisclass__')
        su_obj = object.__getattribute__(self, '__self__')
        su_obj_type = object.__getattribute__(self, '__self_class__')

        starttype = su_obj_type

        # If asked for the __class__ don't go looking for it in the MRO!
        if name == '__class__':
            return object.__getattribute__(self, '__class__')
        mro = starttype.mro()
        n = len(mro)

        # Jump ahead in the MRO to the passed in class 
        # excluding the last one because that is skipped anyway.
        for i in range(0, n - 1):
            if mro[i] is su_type:
                break
        # The C code only increments by one here, but the C for loop
        # actually increases the i variable by one before the condition
        # is checked so to get the equivalent code one needs to increment
        # twice here.
        i += 2
        # We're at the end of the MRO. Check if super has this attribute.
        if i >= n:
            return object.__getattribute__(self, name)

        # Go up the MRO
        while True:
            tmp = mro[i]
            dict_ = tmp.__dict__
            try:
                res = dict_[name]
            except:
                pass
            else:
                # We found a match, now go through the descriptor protocol
                # so that we get a bound method (or whatever is applicable)
                # for this attribute.
                f = type(res).__get__
                f(res, None if su_obj is starttype else su_obj, starttype)
                res = tmp
                return res

            i += 1
            # Not really the nicest construct but it's a do-while loop
            # in the C code and I feel like this is the closest Python
            # representation of that.
            if i < n:
                continue
            else:
                break

        return object.__getattribute__(self, name)

正如您所看到的,有些方法可以最终在super上查找属性:

  • 如果您正在寻找__class__属性
  • 如果您立即到达MRO的末尾(通过传递object作为第一个参数)!
  • 如果__getattribute__在剩余的MRO中找不到匹配项。

实际上因为它的工作方式与super类似,所以您可以使用它(至少就属性委托而言):

class X(object):
    def __init__(self):
        print(MySuper(object, self).__repr__())

X()

这将打印hoho中的MySuper.__repr__。通过插入一些print来跟随控制流,随意尝试该代码。

  

我想知道任何可靠的材料都提到它以及它是否适用于其他Python实现。

我上面所说的是基于我对CPython 3.6源代码的观察,但我认为它应该与其他Python版本不同,因为其他Python实现(通常)遵循CPython。

事实上我也检查过:

所有这些都会返回__repr__的{​​{1}}。

请注意,Python遵循“我们都同意成人”的风格,所以如果有人打算将这些不寻常的用法正式化,我会感到惊讶。我的意思是谁会尝试委托给super(“终极”父类)的兄弟或父类的方法。

答案 1 :(得分:0)

super定义了一些自己的属性,需要一种方法来提供对它们的访问。首先使用__dunder__样式,Python为自己保留,并且说没有库或应用程序应该定义以双下划线开头和结尾的名称。这意味着super对象可以确信任何内容都不会与__self____self_class____thisclass__的属性冲突。因此,如果它搜索mro并且找不到所请求的属性,那么它将重新尝试在超级对象本身上找到该属性。例如:

>>> class A:
    pass

>>> class B(A):
    pass

>>> s = super(A, B())
>>> s.__self__
<__main__.B object at 0x03BE4E70>
>>> s.__self_class__
<class '__main__.B'>
>>> s.__thisclass__
<class '__main__.A'>

由于您已指定object作为开始超越的类型,因为object 总是 mro中的最后一个类型,因此没有可能的候选对象获取方法或属性。在这种情况下,超级行为似乎已经尝试了各种类型寻找名称,但没有找到一个。因此它尝试从自身获取属性。但是,由于super对象也是一个对象,因此它可以访问__init____repr__以及其他所有object定义的对象。因此,super会为您返回自己的__init____repr__方法。

这是一种“问一个愚蠢的问题(超级)并得到一个愚蠢的答案”的情况。也就是说,super只能用第一个参数作为函数定义的类来调用。当你用object调用它时,你会得到未定义的行为。