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

2024-09-29 23:30:38 发布

您现在位置: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()

在本例中,您尝试将clstree和您自己的clstree对象组合在一起。因为clstree忘记在其构造函数中调用super(),所以他们的孩子在使用super()时无法执行clstree的构造函数。你知道吗

因此,输出将如下所示:

Here's Three
Here's Four

显然,我可以手动调用ClsFour的每一个基,而不是使用super(),但是当这个问题分散在我的代码库中时就有点复杂了。你知道吗

顺便说一句,黑匣子的东西是PySide:)

副刊:

多亏了@WillemVanOnsem和@RaymondHettinger,前面的问题就解决了。但经过进一步的调查,我发现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>

它不打印“这里是MyObject”,super()的init属性也有不同的类型。以前,我试图将这个问题简化为ClsFour。但现在我觉得不完全一样了。你知道吗

我猜问题发生在shiboken图书馆,但我不确定。你知道吗

提示:

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


Tags: selfhereobjectinitdefclasspysideprint
3条回答

关于特定于PySide/PyQt4的问题:一种解决方案是确保任何mixin始终在基类定义中位于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,所以这里不会出现这个问题)。你知道吗

摘要

Because ClsThree forgets to call super() in its constructor, their kid cannot execute ClsThreee's constructor when it uses super().

这在Super Considered Super博客文章的“如何合并非合作类”一节中有介绍。你知道吗

关键是通过调用super()创建一个按规则运行的adapter class来协同工作。使用该适配器包装原始类。你知道吗

编制代码

在下面的代码中,AdaptThree是新的适配器类,ClsFour 现在继承自adaptethree而不是原来的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看起来也是不合作的,也应该进行包装。

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

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

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.xx不是对象的属性,也不是ClsFour对象的属性,它将再次遍历MRO以搜索x。你知道吗

如果要调用all__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()并不必要地指向该阶级的父母、祖父母和/或祖先。但是对于对象的父类。你知道吗

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

相关问题 更多 >

    热门问题