gpt4 book ai didi

python - 如何使 super() 在 python 中的这种非理想情况下工作?

转载 作者:太空宇宙 更新时间:2023-11-03 15:53:01 25 4
gpt4 key购买 nike

class ClsOne(object):
def __init__(self):
super(ClsOne, self).__init__()
print "Here's One"

class ClsTwo(ClsOne):
def __init__(self):
super(ClsTwo, self).__init__()
print "Here's Two"

class ClsThree(ClsTwo): # Refer to one blackbox object
def __init__(self):
# super(ClsThree, self).__init__()
print "Here's Three"

class ClsThreee(ClsTwo): # Refer to your custom object
def __init__(self):
super(ClsThreee, self).__init__()
print "Here's Threee"

class ClsFour(ClsThree, ClsThreee): # Multiple Inheritance
def __init__(self):
super(ClsFour, self).__init__()
print "Here's Four"

entity = ClsFour()

在这种情况下,您正在尝试将 ClsThree(它来自一个已编译的库并且很难更改)和您自己的 ClsThreee 对象组合在一起。因为 ClsThree 忘记在其构造函数中调用 super(),他们的 child 在使用 super() 时无法执行 ClsThreee 的构造函数。

结果,输出将是这样的:

Here's Three
Here's Four

显然,我可以手动调用 ClsFour 的每个基,而不是使用 super(),但是当这个问题散布在我的代码库中时,它有点复杂。

顺便说一句,那个黑盒是 PySide :)

补充:

感谢@WillemVanOnsem 和@RaymondHettinger,解决了之前的 ClsFour 问题。但是通过一些进一步的调查,我发现 PySide 中的类似问题没有相同的概念。

在 ClsFour 上下文中,如果您尝试运行:

print super(ClsFour, self).__init__

你会得到:

<bound method ClsFour.__init__ of <__main__.ClsFour object at 0x00000000031EC160>>

但在以下 PySide 上下文中:

import sys
from PySide import QtGui

class MyObject(object):
def __init__(self):
super(MyObject, self).__init__()
print "Here's MyObject"

class MyWidget(QtGui.QWidget, MyObject):
def __init__(self):
super(MyWidget, self).__init__()

app = QtGui.QApplication(sys.argv)
widget = MyWidget()
print super(MyWidget, widget).__init__

结果是:

<method-wrapper '__init__' of MyWidget object at 0x0000000005191D88>

它不打印“Here's MyObject”并且 super() 的 init 属性也有不同的类型。以前,我尝试将此问题简化为 ClsFour。但现在我觉得不完全一样。

我猜问题出在 shiboken 库中,但我不确定。

提示:

这个问题也出现在PyQt上下文中,不过你可以让MyObject继承自QObject来解决。此解决方案在 PySide 中无用。

最佳答案

super() 是一个代理 对象,它使用方法解析顺序 (MRO) 来确定什么调用 super() 时调用的方法。

如果我们检查 ClassFour__mro__,我们得到:

>>> ClsFour.__mro__
(<class '__main__.ClsFour'>, <class '__main__.ClsThree'>, <class '__main__.ClsThreee'>, <class '__main__.ClsTwo'>, <class '__main__.ClsOne'>, <type 'object'>)

或者我自己缩短了它(不是 Python 输出):

>>> ClsFour.__mro__
(ClsFour, ClsThree, ClsThreee, ClsTwo, ClsOne, object)

现在 super(T,self) 是一个代理对象,它使用来自(但不包括)T 的 MRO。所以这意味着 super(ClsFour,self) 是一个代理对象,它适用于:

(ClsThree, ClsThreee, ClsTwo, ClsOne, object)  # super(ClsFour,self)

如果查询类的属性(方法也是属性),Python 将遍历 MRO 并检查元素是否具有此类属性。所以它会首先检查ClsThree是否有__init__属性,如果没有它会继续在ClsThreee中寻找它等等。从找到此类属性的那一刻起,它将停止并返回它。

因此 super(ClsFour,self).__init__返回 ClsThree.__init__ 方法。 MRO 还用于查找未在类级别定义的方法、属性等。因此,如果您使用 self.x 并且 x 不是对象的属性,也不是 ClsFour 对象的属性,它将再次遍历 MRO搜索 x

如果你想将 所有__init__ 称为 ClassFour直接父级,你可以使用:

class ClsFour(ClsThree, ClsThreee):
def __init__(self):
# call *all* *direct* parents __init__
for par in ClsFour.__bases__:
par.__init__(self)

这可能是最优雅的,因为如果基础发生变化,它仍然可以工作。请注意,您必须确保每个父级都存在 __init__。然而,由于它是在 object 级别定义的,因此我们可以放心地假设这一点。然而,对于其他属性,我们不能做出这样的假设。

编辑:请注意,super() 不需要指向该类的 parent 、祖 parent 和/或祖先.但是对于对象的父类。

ClsThree 类中的 super(ClsThree,self) 将 - 假设它是一个 ClsFour 对象,与 same mro(因为它从 self 获取了 mro)。所以 super(ClsThree,self) 将检查以下类序列:

(ClsThreee, ClsTwo, ClsOne, object)

例如,如果我们写(超出任何类的范围)super(ClsTwo,entity).__init__(),我们得到:

>>> super(ClsTwo,entity).__init__()
Here's One
>>> super(ClsThree,entity).__init__()
Here's Two
Here's Threee
>>> super(ClsThreee,entity).__init__()
Here's Two
>>> super(ClsFour,entity).__init__()
Here's Three

关于python - 如何使 super() 在 python 中的这种非理想情况下工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45378208/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com