如何使 super() 在 python 的 non-ideal 情况下工作?
How to make super() work in this non-ideal situation in python?
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(来自单个编译库并且很难更改)和您自己的 ClsThreeeobject在一起。因为 ClsThree 忘记在其构造函数中调用 super(),所以他们的孩子在使用 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()
是一个 proxy object 使用 Method Resolution Order (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 的代理 object。因此,这意味着 super(ClsFour,self)
是一个代理 object,适用于:
(ClsThree, ClsThreee, ClsTwo, ClsOne, object) # super(ClsFour,self)
如果你查询一个class的属性(一个方法也是一个属性)会发生什么,Python会遍历MRO并检查元素是否有这样的属性。所以它会首先检查ClsThree
是否有__init__
属性,如果没有它会继续在ClsThreee
中寻找它等等。从找到这样的属性的那一刻起,它就会停止,return它。
所以super(ClsFour,self).__init__
将returnClsThree.__init__
方法。 MRO还用于查找未在class级别上定义的方法、属性等。因此,如果您使用 self.x
并且 x
不是 object 的属性,也不是 ClsFour
object 的属性,它将再次遍历 MRO 以搜索x
.
如果你想调用全部的__init__
和[=19]的直接parent =] 你可以使用:
class ClsFour(ClsThree, ClsThreee):
def __init__(self):
# call *all* *direct* parents __init__
for par in ClsFour.__bases__:
par.__init__(self)
这可能是最优雅的,因为如果碱基发生变化,它仍然有效。请注意,您必须确保每个 parent 都存在 __init__
。然而,由于它是在 object
级别定义的,因此我们可以放心地假设这一点。然而,对于其他属性,我们不能做出这样的假设。
编辑:请注意,结果 super()
没有 指向 parent 的必要点, class 的祖父parentand/or。但是要 parent classes 的 object.
ClsThree
class 中的 super(ClsThree,self)
将 - 鉴于它是 ClsFour
object,与 相同mro(因为它从 self
中获取 mro
)。所以 super(ClsThree,self)
将检查以下 classes 序列:
(ClsThreee, ClsTwo, ClsOne, object)
例如,如果我们写(超出任何class的范围)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
总结
Because ClsThree forgets to call super() in its constructor, their kid cannot execute ClsThreee's constructor when it uses super().
Super Considered Super 博客 post 的 "How to Incorporate a Non-cooperative Class" 部分对此进行了介绍。
关键是创建一个 adapter class,它通过调用 super() 来按规则合作行动。使用该适配器包装原始 class.
制定的代码
在下面的代码中,AdaptThree
是新适配器 class 和 ClsFour
现在继承自 AdaptThree 而不是原来的黑盒非合作 class.
class ClsOne(object):
def __init__(self):
print "Here's One"
class ClsTwo(ClsOne):
def __init__(self):
print "Here's Two"
class ClsThree(ClsTwo): # Refer to one blackbox object
def __init__(self):
# super(ClsThree, self).__init__()
print "Here's Three"
class AdaptThree(object):
def __init__(self):
_three = ClsThree()
super(AdaptThree, self).__init__()
class ClsThreee(ClsTwo): # Refer to your custom object
def __init__(self):
super(ClsThreee, self).__init__()
print "Here's Threee"
class ClsFour(AdaptThree, ClsThreee): # Multiple Inheritance
def __init__(self):
super(ClsFour, self).__init__()
print "Here's Four"
entity = ClsFour()
这输出:
Here's Three
Here's Two
Here's Threee
Here's Four
其他详情
在 OP 的例子中,ClsTwo 看起来也不合作,也应该包裹起来。
大概这些 classes 有初始化以外的方法。适配器 classes 也需要包装和分派这些调用。
根据应用程序的不同,使用组合可能比继承更容易。
关于 PySide/PyQt4 的特定问题:一种解决方案是确保任何 mixin 始终在基本 class 定义中的 Qt classes 之前:
import sys
from PySide import QtGui
class MyObject(object):
def __init__(self, parent=None, other=None):
super(MyObject, self).__init__(parent)
print "Here's MyObject: %r" % other
class MyWidget(MyObject, QtGui.QWidget):
def __init__(self, parent=None, other=None):
super(MyWidget, self).__init__(parent, other)
app = QtGui.QApplication(sys.argv)
parent = QtGui.QWidget()
widget = MyWidget(parent, 'FOO')
print super(MyWidget, widget).__init__
# check that the QWidget.__init__ was called correctly
print 'widget.parent() is parent:', widget.parent() is parent
输出:
Here's MyObject: 'FOO'
<bound method MyWidget.__init__ of <__main__.MyWidget object at 0x7f53b92cca28>>
widget.parent() is parent: True
(注意:PyQt5 已改进 Support for Cooperative Multi-inheritance,因此此问题不会出现)。
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(来自单个编译库并且很难更改)和您自己的 ClsThreeeobject在一起。因为 ClsThree 忘记在其构造函数中调用 super(),所以他们的孩子在使用 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()
是一个 proxy object 使用 Method Resolution Order (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 的代理 object。因此,这意味着 super(ClsFour,self)
是一个代理 object,适用于:
(ClsThree, ClsThreee, ClsTwo, ClsOne, object) # super(ClsFour,self)
如果你查询一个class的属性(一个方法也是一个属性)会发生什么,Python会遍历MRO并检查元素是否有这样的属性。所以它会首先检查ClsThree
是否有__init__
属性,如果没有它会继续在ClsThreee
中寻找它等等。从找到这样的属性的那一刻起,它就会停止,return它。
所以super(ClsFour,self).__init__
将returnClsThree.__init__
方法。 MRO还用于查找未在class级别上定义的方法、属性等。因此,如果您使用 self.x
并且 x
不是 object 的属性,也不是 ClsFour
object 的属性,它将再次遍历 MRO 以搜索x
.
如果你想调用全部的__init__
和[=19]的直接parent =] 你可以使用:
class ClsFour(ClsThree, ClsThreee):
def __init__(self):
# call *all* *direct* parents __init__
for par in ClsFour.__bases__:
par.__init__(self)
这可能是最优雅的,因为如果碱基发生变化,它仍然有效。请注意,您必须确保每个 parent 都存在 __init__
。然而,由于它是在 object
级别定义的,因此我们可以放心地假设这一点。然而,对于其他属性,我们不能做出这样的假设。
编辑:请注意,结果 super()
没有 指向 parent 的必要点, class 的祖父parentand/or。但是要 parent classes 的 object.
ClsThree
class 中的 super(ClsThree,self)
将 - 鉴于它是 ClsFour
object,与 相同mro(因为它从 self
中获取 mro
)。所以 super(ClsThree,self)
将检查以下 classes 序列:
(ClsThreee, ClsTwo, ClsOne, object)
例如,如果我们写(超出任何class的范围)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
总结
Because ClsThree forgets to call super() in its constructor, their kid cannot execute ClsThreee's constructor when it uses super().
Super Considered Super 博客 post 的 "How to Incorporate a Non-cooperative Class" 部分对此进行了介绍。
关键是创建一个 adapter class,它通过调用 super() 来按规则合作行动。使用该适配器包装原始 class.
制定的代码
在下面的代码中,AdaptThree
是新适配器 class 和 ClsFour
现在继承自 AdaptThree 而不是原来的黑盒非合作 class.
class ClsOne(object):
def __init__(self):
print "Here's One"
class ClsTwo(ClsOne):
def __init__(self):
print "Here's Two"
class ClsThree(ClsTwo): # Refer to one blackbox object
def __init__(self):
# super(ClsThree, self).__init__()
print "Here's Three"
class AdaptThree(object):
def __init__(self):
_three = ClsThree()
super(AdaptThree, self).__init__()
class ClsThreee(ClsTwo): # Refer to your custom object
def __init__(self):
super(ClsThreee, self).__init__()
print "Here's Threee"
class ClsFour(AdaptThree, ClsThreee): # Multiple Inheritance
def __init__(self):
super(ClsFour, self).__init__()
print "Here's Four"
entity = ClsFour()
这输出:
Here's Three
Here's Two
Here's Threee
Here's Four
其他详情
在 OP 的例子中,ClsTwo 看起来也不合作,也应该包裹起来。
大概这些 classes 有初始化以外的方法。适配器 classes 也需要包装和分派这些调用。
根据应用程序的不同,使用组合可能比继承更容易。
关于 PySide/PyQt4 的特定问题:一种解决方案是确保任何 mixin 始终在基本 class 定义中的 Qt classes 之前:
import sys
from PySide import QtGui
class MyObject(object):
def __init__(self, parent=None, other=None):
super(MyObject, self).__init__(parent)
print "Here's MyObject: %r" % other
class MyWidget(MyObject, QtGui.QWidget):
def __init__(self, parent=None, other=None):
super(MyWidget, self).__init__(parent, other)
app = QtGui.QApplication(sys.argv)
parent = QtGui.QWidget()
widget = MyWidget(parent, 'FOO')
print super(MyWidget, widget).__init__
# check that the QWidget.__init__ was called correctly
print 'widget.parent() is parent:', widget.parent() is parent
输出:
Here's MyObject: 'FOO'
<bound method MyWidget.__init__ of <__main__.MyWidget object at 0x7f53b92cca28>>
widget.parent() is parent: True
(注意:PyQt5 已改进 Support for Cooperative Multi-inheritance,因此此问题不会出现)。