关于python:为什么super在pyside/pyqt中使用这么多?

Why is super used so much in PySide/PyQt?

短版(TL;DR)

我正在学习Pyside,大多数在线教程都使用super来初始化UI元素。这一点很重要(即更具可扩展性),还是出于品味?

澄清:正如我在详细版本中所说的,这不是另一个询问何时使用super的通用线程(这以前已经做过)。相反,考虑到使用super而不是.__init__的pyside教程的数量,我试图弄清楚在pyside应用程序中使用super是否是标准的?如果是这样的话,是否是因为在使用pyside/pyqt时,需要(涉及解决继承)super的情况特别多?或者是品味问题。

详细版本

我对python不熟悉,目前正在使用zets教程(http://zetcode.com/gui/pysidethotorial/firstprograms/)学习pyside。本教程中的第二个示例包括:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from PySide import QtGui

class Example(QtGui.QWidget):
    def __init__(self):      
        super(Example, self).__init__()
        self.initUI()
    def initUI(self):
        self.setGeometry(300,300,250,150)
        self.setWindowTitle("PySide 101: Window the First!")
        self.show()

app=QtGui.QApplication(sys.argv)
ex=Example()
sys.exit(app.exec_())

这很好,但我从来没有用过super。因此,我重写了上述代码,成功地用更标准的父类显式调用替换了super

1
QtGui.QWidget.__init__(self)

但是,当我在网上搜索Pyside教程(例如http://qt project.org/wiki/Pyside newbie tutorials)时,它们都包括对super的调用。我的问题是:我应该使用super来编写Pyside脚本吗?

当您拥有继承钻石时,super似乎最有用,它倾向于以合理的方式解决多重继承的情况。super与Pyside一起使用得多吗?因为这种钻石有很多案例,我将面对更现实、更复杂的例子?[编辑:否:请参阅下面的答案。]

为什么我还要问?为什么不直接用super来完成呢?

我之所以这样问是因为我用来学习Python的那本书(Learning python,by lutz)花了20多页讨论了super的主题,并明确警告不要使用它。他建议新的python用户在处理之前使用更传统、更明确的路径(例如,参见第832页和第1041-1064页学习python,第5版)。他基本上把它描绘成一种非物质的,神秘的,很少实际需要的,新的风格,在刚开始的时候你应该谨慎对待,并且认为它被经验丰富的用户过度使用。

此外,查看两个基于Pyside/Pyqt的主要项目(Spyder和PyqTgraph)的源代码,它们都不使用super。其中一个(spyder)明确地告诉参与者出于兼容性的原因避免使用它(http://code.google.com/p/spyderlib/wiki/noteforcontributors)。

注意,我链接到下面一篇密切相关的文章,但是这里的答案更广泛地讨论了当您想要使用super(当您有多个继承时)。我的问题是,从长远来看,Pyside脚本是否有理由,甚至需要使用super,或者出于兼容性的原因,是否更像Pyshonic,更好地显式命名父类?或者是品味问题?

如果不喜欢(就像我的初学者书中提到的那样),为什么Pyside针对初学者的教程中如此普遍?如果它有什么不同的话,似乎编写这些教程的人是经验丰富的Java程序员,或者迎合这些程序员。我不是。

相关主题

http://www.riverbankcomputing.com/pipermail/pyqt/2008-1月/018320.html

对pyqt4使用uu init_uuu的不同方法

使用uu init_uu()方法了解python super()。


嗯,不错。但在我看来,这只是和qt/pyside几乎没有关系。

首先,这两个有什么不同?如果您有简单的继承(可能不包括"mixin"),那么在行为上就没有区别。一个表面上的区别仍然存在——你不需要再次命名你的基类——但是你必须命名同一个类。

当您有多个继承时,差异就开始了。然后,一个EDOCX1[0]链需要这个层次结构:

1
2
3
4
5
          A
        /   \
       X     Y
        \   /
          Z

可以通过super()呼叫轻松进行如下操作:

1
2
3
4
5
          A
            \
       X --- Y
        \  
          Z

不需要X和Y互相了解。这与方法解析顺序的概念有关,它允许在Python文档中使用一种称为"合作多继承"的编程风格。

如果有一个方法Foo,并且该方法的x和y实现建立在a的实现之上,那么z可以很容易地依赖x和y,而不需要它们彼此了解。然而,这有一个重要的前提:Foo在每个类中都有相同(或至少[兼容])的签名,正如A最初定义它的接口所指定的那样。

__init__方法是特殊的:技术上它与super的工作方式完全相同,但是!但是(通常情况下)对于子类,它有一个完全不同的签名。如果子类"EDOCX1"(5)看起来不同,那么super不会通过显式的基本调用给您任何东西,因为您无论如何都不能使用协作多任务。

注意:python在这方面是非常不典型的:在大多数OO语言中,构造函数属于类,而不是实例;换句话说,大多数语言依赖于它们等价的__new__,根本没有__init__

注2:我从未见过任何真正的依赖于合作多重继承的代码。(单一继承很容易为我做足够的意大利面;-)

另外,一些好的读物:

  • 方法分辨率顺序-这是关于什么的
  • Python的超棒很漂亮,但你不能用它

[


用传统的方法实例化父类并没有什么错,有些事情应该说出来支持它。也就是说,使用super简化了子类的创建,以及将来对Pyside代码的修改,人们似乎已经将后者作为首要因素。这不是特定于Pyside,而是更一般地针对Python中的面向对象编程(如Kos的最佳答案中所指出的)。

简化代码修改的可能性是因为在Pyside中,有必要根据其他QtGui对象(例如QtQui.QMainWindowQtGui.QWidget)定义子类。此外,人们倾向于与他们的父类进行充分的混合,这样就更容易使用super,这样你就不必每次改变父类时都更新你的初始化方法。

因此,使用super来帮助解决多重继承的问题并不重要,大多数人都认为这是最合适的。相反,如果您的父类将来发生变化,则需要减少在初始化中的工作。

以下是每个作者的回答,他们都写了我认为很好的Pyside教程:

作者1:

I think it is a matter of taste. Earlier tutorials (PyQt and PySide)
used .init and later I switched to super(). I personally
prefer super().

作者2:

The reason people use super instead of .init(...) is to
avoid making changes if you change what the parent class is, e.g. if
you switch from QVBoxLayout to QHBoxLayout, you only have to change it
in the class definition line, rather than in the init method as
well.

所以你有了它。这些好处并不是针对Pyside的,而是更广泛地编写子类/继承。

我不确定卢茨会说什么,他似乎很犹豫是否同意使用super,可能使用super违反了"显式优于隐式"的准则。

四年后更新回想起来,这场辩论已经结束了,这个问题几乎是古怪的(这是我第一个这样的问题)。虽然过去有关于使用super的争论,但这些争论已经结束了。尤其是在python 3中,super的便利性已经证明了它的存在,并且使代码更容易维护。因为在qt/pyside/pyqt框架中,从更抽象的qt类继承的使用无处不在,所以这不是一个小特性。当然,当你有疯狂的继承格时,你需要小心,但是坦白地说,自从我问这个问题以来,我从来没有遇到过这个问题,我现在在我的所有代码中都使用super。它可能违反了"显式优于隐式"的格言,但"简单优于复杂"和"实用胜过纯粹"是这里的首要因素(这里的实际方面是"可维护性计数")。