当类型为 'object' 时 'super()' 的行为?
The behaviour of 'super()' when type is 'object'?
在 Python website says it returns a proxy object that delegates method calls to parent or sibling class. Information found at super considered super, how does super() work with multiple inheritance and super considered harmful 上找到的关于 super()
的文档说明实际上使用了 mro
中的下一个方法。我的问题是,如果使用 super(object, self).some_method()
会怎样?由于 object
通常出现在 mro
列表的末尾,我猜搜索将立即结束,但出现异常。但实际上好像是调用了proxy本身的方法,如super(object, self).__repr__()
显示super对象本身。我想知道 super()
和 object
的行为是否是 根本不委托方法 。
如果是这种情况,我想知道任何可靠的 material 曾经提到过它以及它是否适用于其他 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()
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 的行为就好像它已经尝试了各种类型来寻找名称,但没有找到一个。所以它试图从自身获取属性。但是,由于 super
对象也是一个对象,它可以访问 __init__
、__repr__
以及 object
定义的所有其他对象。因此 super
returns 它自己的 __init__
和 __repr__
方法供您使用。
这是"ask a silly question (of super) and get a silly answer"的一种情况。也就是说, super
应该只用第一个参数作为定义函数的 class 来调用。当你用 object
调用它时,你会得到未定义的行为。
如果 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()
这将从 MySuper.__repr__
打印 hoho
。通过插入一些 print
来跟随控制流,随意试验该代码。
I wonder any reliable material ever mentions it and whether it applies to other Python implementations.
我上面所说的是基于我对 CPython 3.6 源代码的观察,但我认为对于其他 Python 版本应该不会有太大差异,因为另一个 [=76] =] 实现(通常)遵循 CPython。
其实我也查过:
所有 return super
的 __repr__
。
请注意 Python 遵循 "We are all consenting adults" 风格,所以如果有人费心将这种 不寻常的 用法正式化,我会感到惊讶。我的意思是谁会尝试委托给 object
的兄弟或父 class 的方法("ultimate" 父 class)。
在 Python website says it returns a proxy object that delegates method calls to parent or sibling class. Information found at super considered super, how does super() work with multiple inheritance and super considered harmful 上找到的关于 super()
的文档说明实际上使用了 mro
中的下一个方法。我的问题是,如果使用 super(object, self).some_method()
会怎样?由于 object
通常出现在 mro
列表的末尾,我猜搜索将立即结束,但出现异常。但实际上好像是调用了proxy本身的方法,如super(object, self).__repr__()
显示super对象本身。我想知道 super()
和 object
的行为是否是 根本不委托方法 。
如果是这种情况,我想知道任何可靠的 material 曾经提到过它以及它是否适用于其他 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()
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 的行为就好像它已经尝试了各种类型来寻找名称,但没有找到一个。所以它试图从自身获取属性。但是,由于 super
对象也是一个对象,它可以访问 __init__
、__repr__
以及 object
定义的所有其他对象。因此 super
returns 它自己的 __init__
和 __repr__
方法供您使用。
这是"ask a silly question (of super) and get a silly answer"的一种情况。也就是说, super
应该只用第一个参数作为定义函数的 class 来调用。当你用 object
调用它时,你会得到未定义的行为。
如果 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()
这将从 MySuper.__repr__
打印 hoho
。通过插入一些 print
来跟随控制流,随意试验该代码。
I wonder any reliable material ever mentions it and whether it applies to other Python implementations.
我上面所说的是基于我对 CPython 3.6 源代码的观察,但我认为对于其他 Python 版本应该不会有太大差异,因为另一个 [=76] =] 实现(通常)遵循 CPython。
其实我也查过:
所有 return super
的 __repr__
。
请注意 Python 遵循 "We are all consenting adults" 风格,所以如果有人费心将这种 不寻常的 用法正式化,我会感到惊讶。我的意思是谁会尝试委托给 object
的兄弟或父 class 的方法("ultimate" 父 class)。