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

时间:2017-07-28 16:19:36

标签: python pyside multiple-inheritance super python-sip

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>

它没有打印&#34;这里的MyObject&#34;而且super()的init属性也有不同的类型。以前,我尝试将此问题简化为ClsFour。但现在我认为它并不完全相同。

我猜这个问题发生在shiboken库中,但我不确定。

TIPS:

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

3 个答案:

答案 0 :(得分:3)

super()代理对象,它使用 方法解析订单(MRO) 来确定您在调用时调用的方法在super()上进行通话。

如果我们检查__mro__的{​​{1}},我们会得到:

ClassFour

或者让自己缩短(不是Python输出):

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

现在>>> ClsFour.__mro__ (ClsFour, ClsThree, ClsThreee, ClsTwo, ClsOne, object) 是一个代理对象,它使用(但不包括)super(T,self)的MRO。这意味着T是一个可以使用的代理对象:

super(ClsFour,self)

如果查询类的属性(方法也是属性)会发生什么,Python将遍历MRO并检查元素是否具有此属性。因此,它将首先检查(ClsThree, ClsThreee, ClsTwo, ClsOne, object) # super(ClsFour,self) 是否具有ClsThree属性,否则它将继续在__init__中查找,依此类推。从找到这样的属性的那一刻起,它就会停止并返回它。

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

如果您想拨打所有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()类中的super(ClsThree,self),如果是ClsThree对象,则使用相同的mro (因为它需要ClsFour来自mro)的1}}。因此self将检查以下类序列:

super(ClsThree,self)

例如,如果我们写(超出任何类的范围)(ClsThreee, ClsTwo, ClsOne, object) ,我们得到:

super(ClsTwo,entity).__init__()

答案 1 :(得分:0)

摘要

  

因为ClsThree忘记在其构造函数中调用super(),所以当他们使用super()时,他们的孩子不能执行ClsThreee的构造函数。

&#34;如何合并非合作班级&#34; Super Considered Super博文的一部分。

关键是要创建一个adapter class,通过调用 super()来遵守规则以协同行动。使用该适配器包装原始类。

已完成的代码

在下面的代码中,AdaptThree是新的适配器类, ClsFour  现在继承自 AdaptThree 而不是原始的blackbox非合作类。

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 看起来也是非合作的,也应该包装好。

  • 据推测,这些类具有初始化以外的方法。适配器类也需要包装和分派这些调用。

  • 根据应用程序的不同,使用构图而不是继承可能更容易。

答案 2 :(得分:0)

关于PySide / PyQt4特有的问题:一个解决方案是确保任何mixins总是在基类定义中的Qt类之前:

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,所以这个问题并没有出现。)