Roger Stuckey的wxPython多进程示例代码中'self.update'的用法是什么
我在看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类中processTasks和processStop这两个函数的签名有关。但我真的不知道怎么把参数self.update和可选参数resfunc联系起来。
我觉得Roger的代码没有问题。但如果我不能改动源代码来测试我的理解,那让我很困扰。
我在Windows 7上使用Python 2.7。
1 个回答
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>>
)。