问题 为什么在PySide / PyQt中超级使用了这么多?


短版(tl; dr)

我正在学习PySide,大多数在线教程都使用 super 初始化UI元素。这是重要的(即,更具可扩展性),还是品味问题?

澄清:正如我在详细版本中更清楚,这不是另一个通用线程,询问何时使用 super (这是以前做过的)。相反,考虑到使用的PySide教程的数量 super 代替 <class>.__init__,我想弄清楚是否使用 super 是PySide应用程序的标准?如果是这样,是因为情况所在 super 被称为(涉及解决继承)在PySide / PyQt的使用中出现了很多?或者这是品味问题。

详细版本

我是Python新手,目前正在使用Zets教程学习PySide(http://zetcode.com/gui/pysidetutorial/firstprograms/)。本教程中的第二个示例包括:

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 更多标准显式调用父类:

QtGui.QWidget.__init__(self)

但是当我在网上搜索PySide教程时(例如, http://qt-project.org/wiki/PySide-Newbie-Tutorials), 他们 所有 包括打电话 super。我的问题是:我应该使用 super 用于PySide脚本?

看起来 super 当你有继承钻石时,它似乎最有用,它倾向于以合理的方式解决多重继承的实例。是 super PySide使用了很多,因为这些钻石的案例占优势,我会面对更现实的复杂例子吗? [编辑:否:请参阅下面的答案。]

我为什么要问? 为什么不用 super 并完成它?

我问,因为我用来学习Python的书(学习Python,由Lutz撰写)花费了20多页关于 super,和 明确警告不要使用它。他建议新的Python用户在使用它之前采用更传统,更明确的路线(例如,参见第832页,学习Python的第1041-1064页,第5版)。他基本上将它描绘成一种非剧性的,神秘的,很少需要的新风格,刚开始时你应该非常谨慎地对待它,并认为它被有经验的用户过度使用。

此外,查看两个主要的PySide / PyQt项目(Spyder和pyqtgraph)的源代码,两者均未使用 super。 One(Spyder)明确告诉贡献者出于兼容性原因而避免使用它(http://code.google.com/p/spyderlib/wiki/NoteForContributors)。

注意我链接到下面一个密切相关的帖子,但那里的答案更普遍地讨论你想要使用 super (当你有多重继承时)。我的问题是PySide脚本是否合理,甚至要求使用 super从长远来看,或者是否更具Pythonic,并且出于兼容性原因,更明确地命名父类?还是味道问题?

如果它不受欢迎(正如我的初学者所说)为什么它在针对初学者的PySide教程中如此普遍?如果它有所不同,那么编写这些教程的人似乎都是经验丰富的Java程序员,或者适合这样的程序员。我不是。

相关话题

http://www.riverbankcomputing.com/pipermail/pyqt/20​​08-January/018320.html

使用__init__ for PyQt4的不同方法

使用__init __()方法理解Python super()


13042
2018-06-01 15:50


起源

就个人而言,我用 <class>.__init(self,...),但是当我最初进行子类化时,我最近被它咬了(让我们说) QWidget 并覆盖了一堆方法,在某个时刻调用父类方法。后来我把我的班级改为子类 QMainWindow 并忘了改变所有的 QWidget.<method> 打电话来。运用 super() 本来会更容易。 - three_pineapples
@three_pineapples所以这不是继承解决的情况,但主要是代码重构之一?有趣。 - neuronet
不,这是继承解决(我称之为继承解决方案)。在复杂的子类中,您不仅仅想要调用 parent_cls.__init__ 但是当你扩展功能时的其他方法(例如你的实现) getText() 可能想打电话 parent_cls.getText())。运用 super 避免在您使用它的每个地方手动更新parent_cls。 - three_pineapples
右:我的意思是多重继承(钻石等)。 - neuronet
你好2017年! 如今 语法就好了 super().__init__() 甚至更短 BaseClass.__init__(self) - Kos


答案:


嗯,很好。但IMO与Qt / PySide几乎没有关系。

首先,这两者有何不同?如果你有简单的继承(也许不算“mixins”),那么行为没有区别。化妆品差异仍然存在 - 您不需要再次命名基类 - 但您必须命名同一个类。

当您有多个继承时,差异就会开始。然后是一个链 super() 调用此层次结构:

          A
        /   \
       X     Y
        \   /
          Z

这样可以轻松地进行 super() 要求:

          A
            \
       X --- Y
        \   
          Z

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

如果有方法的话 Foo 并且该方法的X和Y中的实现基于A的实现,然后Z很容易依赖于X和Y,而他们甚至不知道彼此。然而,这有一个重要的前提条件: Foo 在每个类中具有相同(或至少[兼容])签名,如下所述 A它最初定义的界面。

__init__ 方法很特殊:从技术上讲,它的工作方式完全相同 super,但是!但是(通常情况下),对于子类,它具有完全不同的签名。如果是子类' __init__ 那么看起来不一样 super 因为你无论如何都无法使用合作多任务处理,所以不会给你任何明确的基本调用。

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

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

还有一些好的阅读:

[


7
2018-06-01 16:47



Upvote可以很好地解释多继承类型的情况。然而,我的问题的关键是与PySide的关系。这种情况是否经常在PySide编程中发生,以使其成为应该完成的事情,这是否解释了为什么super在初学者PySide教程中无处不在?也就是说,在PySide脚本编写环境中,这种情况经常发生,以至于不使用super会很愚蠢吗?或者它是大多数教程奇怪地以Java为中心(超级更自然)的事实的副产品? - neuronet
我猜这个普遍使用的事实是因为一旦你决定使用它 super, 而不是 <class>.__init__,这是更好的风格,以保持一致,而不是在所有可能的变化之间切换。您的第一个相关主题中描述的“混淆”清楚地证明了这一点。 - sebastian
好像它基本上是一个品味的问题,所以因为我害怕超级(来自Lutz的书),我只会做旧的方式。我确实找到了一个最近的教程: pythoncentral.io/series/python-pyside-pyqt-tutorial - neuronet
+1你的签名评论 __init__ 功能。如果签名发生变化,那么你必须打电话给 <class>.__init__ 确保你通过可接受的论点。另外,当我停止使用超级时,我开始对OOP感觉更舒服。 Python的禅宗:“明确胜过隐性”。我也是卢茨的孩子。 =) - Will Martin


在研究了这个问题之后,我在上周与PySide合作,并通过电子邮件发送给PySide的两个教程的作者(其中一个使用super,另一个没有,在教程中)。

即,使用 super 在很大程度上是一种品味问题,因为以传统的方式实例化父类是没有错的,有些事情可以说是赞成它。但是,至少在表面上,使用 super确实似乎简化了对一个人的PySide代码的未来修改,人们似乎已经把它作为最重要的因素。

简化代码修改的可能性是因为在PySide中,通常基于QtGui对象定义子类(例如, QtQui.QMainWindow 和 QtGui.QWidget)。此外,人们倾向于使用足够的父类,以便使用起来更容易 super,这样您每次更换父母时都不必更新__init__功能。

所以这不是使用问题 super 为了帮助解决多重继承的情况,大多数人都同意这种情况可能是最适合的。相反,如果您的父类将来发生变化,则需要在__init__中减少工作量。

以下是每位作者的回复,他们都写了我认为好的PySide教程:

作者1:

我认为这是一个品味问题。早期的教程(PyQt和PySide)   用过的 。在里面 后来我切换到了super()。我个人   更喜欢super()。

作者2:

人们使用super代替的原因。在里面(...)是   如果更改父类的内容,请避免进行更改,例如如果   你从QVBoxLayout切换到QHBoxLayout,你只需要改变它   在类定义行中,而不是在 在里面 方法为   好。

所以你有它。不确定这是否真的特定于PySide,或更普遍地编写子类。

我不确定Lutz是什么,他似乎非常犹豫要赞同使用 super,会说(也许使用 super 对于初学者而言,违反了“明确胜于隐性”的格言。当我试图更好地理解他对该功能的关注时,我将在此上发布更多内容。


5
2018-06-05 15:43



感谢您直接跟随作者。这是非常有用的评论。 - kevinarpe