如何使super()在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(来自单个编译的库,很难更改)和您自己的ClsThreee对象结合在一起.由于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()是一个代理对象,该代理对象使用方法解析顺序(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.

如果要调用ClassFour的所有直接父级的__init__,可以使用:

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

这可能是最优雅的,因为如果更改了基础,它将仍然有效.请注意,您必须确保每个父级都存在__init__.由于它是在对象级别定义的,因此我们可以放心地假设这一点.但是,对于其他属性,我们无法做出该假设.

编辑:请注意,结果super()不一定指向该类的父母,祖父母和/或祖先.但是要对象的父类.

ClsThree类中的super(ClsThree,self)将-由于它是一个ClsFour对象,因此将使用相同的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

转载注明原文:如何使super()在python中的这种非理想情况下工作? - 代码日志