在 python 中编写模块化代码

Writing modular codes in python

您好,我有一个简单的 GUI,如下所示,带有 2 个按钮。我编写了一种方法,可以在单击按钮后更改按钮上的文本。我想使该方法模块化和通用,以便将该方法应用于任何按钮而无需重写。在下面的示例中,如何在不为其定义新方法的情况下将 printWow() 方法应用于按钮 2?

import sys
from PyQt4.Qt import *

class MainWindow(QMainWindow):
    def __init__(self, *args):
        QMainWindow.__init__(self, *args)
        self.cw = QWidget(self)
        self.setCentralWidget(self.cw)
        self.btn1 = QPushButton("Click me", self.cw)
        self.btn1.setGeometry(QRect(50, 50, 100, 30))
        self.btn1.clicked.connect(self.printWow)

        self.btn2 = QPushButton("Click me", self.cw)
        self.btn2.setGeometry(QRect(50, 20, 100, 30))
        self.btn2.clicked.connect(self.printWow)

    def printWow(self):
        self.btn1.setText("WoW")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    myapp = MainWindow()
    myapp.show()
    sys.exit(app.exec_())

您可以使用 sender() 方法确定单击了哪个按钮。

Returns a pointer to the object that sent the signal, if called in a slot activated by a signal; otherwise it returns 0. The pointer is valid only during the execution of the slot that calls this function from this object's thread context.

The pointer returned by this function becomes invalid if the sender is destroyed, or if the slot is disconnected from the sender's signal.

虽然文档中有一个重要警告,它会与您的 post 的标题冲突:

Warning: This function violates the object-oriented principle of modularity. However, getting access to the sender might be useful when many signals are connected to a single slot.


您可以像这样使用 sender()

def printWow(self):
    self.sender().setText("WoW")

基本上,创建函数 "more generic" 意味着识别不同用例中的变体和不变性。在您的示例中,变体将是您要调用 setText() 的 object,最终是文本本身,因此您的 "generic" 函数看起来像:

def printText(self, target, text="wow"):
    target.setText(text)

然后因为您不能将这些参数传递给 connect()(好吧,至少为了我们的示例,我们假设是这种情况),您需要将对通用函数的引用和参数包装在一起,可以在没有参数的情况下调用。这是 "partial evaluation" 的一个例子,最简单的形式只需要 lambda:

class MainWindow(QMainWindow):
    def __init__(self, *args):
        QMainWindow.__init__(self, *args)
        self.cw = QWidget(self)
        self.setCentralWidget(self.cw)
        self.btn1 = QPushButton("Click me", self.cw)
        self.btn1.setGeometry(QRect(50, 50, 100, 30))
        self.btn1.clicked.connect(lambda: self.printWow(self.btn1))

        self.btn2 = QPushButton("Click me", self.cw)
        self.btn2.setGeometry(QRect(50, 20, 100, 30))
        self.btn2.clicked.connect(lambda: self.printText(self.btn2, "Yay!"))

    def printText(self, target, text="wow"):
        target.setText(text)

请注意,在上面的示例中,您不会从额外的复杂性中获益太多 - 您也可以将所有内容都放在 lambda 中,即:

self.btn1.clicked.connect(lambda: self.btn1.setText("Wow"))
self.btn2.clicked.connect(lambda: self.btn2.setText("Yay !"))

但我假设您的想法更复杂...

既然你提到了 "modularity"(至少在你的标题中),"modular" 代码的真正关键不一定(或至少不仅)分解出公共代码,而且主要将您的代码拆分为分离良好的模块,每个模块都有明确且不同的职责。一个典型的例子是将 UI 代码与 "domain" 代码分开 - 执行实际工作的代码甚至根本不知道 UI 存在......