Python '的行为;超级()和#x27;当类型为';对象';?

Python '的行为;超级()和#x27;当类型为';对象';?,python,super,Python,Super,在上找到的关于super()的文档说,它返回一个代理对象,该对象将方法调用委托给父类或同级类。在中找到的信息,并说明实际上使用了mro中的下一种方法。我的问题是,如果使用了super(object,self)。会发生什么?由于object通常出现在mro列表的末尾,因此我猜搜索将立即到达末尾,出现异常。但事实上,代理本身的方法似乎已被调用,如显示超级对象本身的super(object,self.\uu repr\uu()所示。我想知道super()与object的行为是否根本不是委托方法。 如果

在上找到的关于
super()
的文档说,它返回一个代理对象,该对象将方法调用委托给父类或同级类。在中找到的信息,并说明实际上使用了
mro
中的下一种方法。我的问题是,如果使用了
super(object,self)。会发生什么?由于
object
通常出现在
mro
列表的末尾,因此我猜搜索将立即到达末尾,出现异常。但事实上,代理本身的方法似乎已被调用,如显示超级对象本身的
super(object,self.\uu repr\uu()
所示。我想知道
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()
X类(对象):
定义初始化(自):
#这显示了[X,object]。
打印X.mro()
#这显示了超级对象可以具有的一组属性。
打印目录(超级(对象、自身))
#这表明类似于
打印(对象、自身)
#此操作失败,因为'super()至少接受一个参数`
尝试:
超级(对象,自我)。\uuuu初始化
除:
通过
#这显示了类似的情况。
打印(超级(对象,自我)。\uuuu repr\uuuuu()
#这显示了对象的repr(),如
打印(超级(X,自我)。\uuuu repr\uuuu()
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
X()

super
定义了它自己的一些属性,并且需要一种方法来提供对这些属性的访问。首先是使用
\uuuu dunder\uuuu
样式,Python保留了这种样式,并表示任何库或应用程序都不应定义以双下划线开头和结尾的名称。这意味着
super
对象可以确信其
\uuuuuu self\uuuuu
\uuuuuu self\u class\uuuuuu
\uuuuuu This class\uuuuuuuuu
的属性不会发生冲突。因此,如果它搜索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'>
>>A类:
通过
>>>B(A)类:
通过
>>>s=超级(A,B())
>>>萨尔夫__
>>>s.\u self\u类__
>>>这个班的学生__
由于您已将
object
指定为要开始查看的类型,并且
object
始终是mro中的最后一个类型,因此不可能为其获取方法或属性。在这种情况下,super的行为就像它尝试了各种类型来查找名称,但没有找到名称一样。因此,它尝试从自身获取属性。但是,由于
super
对象也是一个对象,因此它可以访问
\uuuuu init\uuuuuu
\uuu repr\uuuuu
以及
对象定义的所有其他内容。因此,
super
为您返回它自己的
\uuuuu init\uuuu
\uuuu repr\uuuu
方法


这是一种“问一个愚蠢的问题(超级)然后得到一个愚蠢的答案”的情况。也就是说,
super
应该只使用第一个参数作为定义函数的类来调用。当您使用
对象调用它时,您将获得未定义的行为。

如果
super
在查看要委托给的方法解析顺序(MRO)时未找到任何内容(或者如果您正在查找属性
\uuuu class\uuuuuu
),它将检查自己的属性

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


我发现这个问题真的很有趣,所以我去了
super
的源代码,特别是委托部分(),我(粗略地)将其翻译成纯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)
这将从
MySuper.\uuu repr\uuu
打印
hoho
。通过插入一些
print
s来跟随控制流,您可以自由地试验该代码

我想知道是否有可靠的材料提到过它,以及它是否适用于其他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()
我上面所说的是基于我对CPython 3.6源代码的观察,但是我认为对于其他Python版本来说应该没有太大的不同,因为其他Python实现(通常)都遵循CPython

事实上,我还检查了:

  • ,
它们都返回
super
\uuuu repr\uuu


请注意,Python遵循“我们都是同意的成年人”风格,因此如果有人费心将这种不寻常的用法形式化,我会感到惊讶。我的意思是,谁会尝试委托给
对象的同级或父类的方法(“最终”父类)。

是的,我知道
super()
可以调用
对象的同级方法,但是这不能改变这样一个事实,
object
出现在mro列表的最后,除非存在旧式类。您确定
super
在返回它自己的属性之前首先遍历mro吗?听起来很傻。反过来做会更有意义。这种情况只能通过显式使用
object
作为参数来产生,这在实践中很少发生。所以我同意你“问愚蠢的问题”的观点。但我不同意“未定义行为”的观点,因为至少它是允许的(没有文档说
类型
不能是
对象
),甚至没有错误(没有引发异常)。这是一个很好的定义,非常深入的研究。我对属性查找机制有了更好的理解。谢谢