Roger Stuckey的wxPython多进程示例代码中'self.update'的用法是什么

0 投票
1 回答
7669 浏览
提问于 2025-04-17 19:18

我在看Roger Stuckey的wxPython多线程代码,想自己做一个类似的程序。完整代码可以在这里找到。

这段代码运行得很好,没有任何修改。不过,我发现有一个参数self.update在GUI类MyFrame和处理类TaskServerMP之间传来传去。我查了整个代码片段,搞不明白这个参数到底是干嘛的——它从来没有被初始化,也没有被使用过。

在MyFrame类中:

def OnStart(self, event):
   ...
   self.taskserver.processTasks(self.update)
   ...

def OnStop(self, event):
   ...
   self.taskserver.processStop(self.update)
   ...

def update(self, output):
    """
    Get and print the results from one completed task.
    """
    self.output_tc.AppendText('%s [%d] calculate(%d) = %.2f\n'...
    ...
    # Give the user an opportunity to interact
    wx.YieldIfNeeded()

在TaskServerMP类中:

def run(self):
   ...
   self.processTasks(self.update)
   ...

def processTasks(self, resfunc=None):
   ...

def processStop(self, resfunc=None): 
   ...

def update(self, output):
    """
    Get and print the results from one completed task.
    """
    sys.stdout.write('%s [%d] calculate(%d) = %.2f' % ....

所以我觉得这可能是依赖注入的做法,但也没什么特别的。我就把它从代码中删掉了,结果发生了奇怪的事情——程序不再工作了!我能看到GUI界面,也能启动处理。但是,GUI界面就卡住了,后来Windows报告说程序没有响应。我最后不得不手动从Windows任务管理器中结束所有的pythonw.exe进程。

然后我开始想,是否和TaskServerMP类中processTasksprocessStop这两个函数的签名有关。但我真的不知道怎么把参数self.update和可选参数resfunc联系起来。

我觉得Roger的代码没有问题。但如果我不能改动源代码来测试我的理解,那让我很困扰。

我在Windows 7上使用Python 2.7。

1 个回答

2

MyFrame.update 是一个方法。你可以在第365行看到它的定义。

所以 self.update 是一个绑定的方法,这意味着它可以像普通函数一样被调用。

你会看到 processTasks 接受一个 resfunc 参数;然后,在第165行,如果这个 resfunc 参数是一个函数或方法,它就会被调用。

这里的意思是 processTasks 让调用者决定如何在每个任务完成时打印进度更新。一个类可能通过将信息写入标准输出(stdout)来实现,而另一个类可能会更新图形界面的进度条。

这是一种在Python代码中传递回调函数的典型方式。


那么,如果你去掉 self.update,为什么程序会卡住呢?看看第372行里面的内容:

    # Give the user an opportunity to interact
    wx.YieldIfNeeded()

在wx和大多数图形用户界面(GUI)框架中,主线程正在运行一个“事件循环”,这个循环需要处理每一个事件(比如鼠标移动、按键等),然后等待下一个事件。你写的代码是一些事件处理程序——当有人点击这个按钮时,就运行那个函数;等等。你的事件处理程序必须快速返回。否则,事件循环就无法接收和处理下一个事件,这样你的图形界面就不会响应。在wx中,Yield 一系列的函数让这一切变得简单。只要你足够频繁地使用 Yield,你就不需要快速返回。但你仍然必须选择其中一种方式——要么提前返回,要么使用 Yield——否则图形界面就会卡住。


这里有一个非常简单的例子,展示了如何使用绑定的方法:

class Foo(object):
    def __init__(self, name):
        self.name = name
    def print_name(self):
        print(self.name)
    def give_me_a_printer_function(self):
        return self.print_name

spam = Foo('Spam')
my_function1 = spam.print_name
my_function2 = spam.give_me_a_printer_function()
my_function1()
my_function2()

这段代码会打印 Spam 两次。

在Python中,函数和方法是 一等值,你可以像传递数字、字符串、列表和类实例一样传递它们。你甚至可以打印它们(虽然你会得到一些看起来很丑的东西,比如 <bound method Foo.print_name of <__main__.Foo object at 0x104629190>>)。

撰写回答