在Python website上找到的关于super()
的文档说它返回一个代理对象,该方法将方法调用委托给父类或兄弟类。在super considered super,how 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()
答案 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__
属性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
调用它时,你会得到未定义的行为。