前一个程序中, 我们介绍了一下lambda技术. 在这个程序中, 我们来看看另一个技术, "currying".
*关于Curry*
简单的说, curry是一项用函数来构造函数的技术.
curry是一个从函数式编程中引入的技术. 如果你想更多的了解currying, 在"python cookbook"会有一些介绍:
本程序中使用的curry类来自于Scott David Daniels的文章"curry -- associating parameters with a function", 该文章地址:
和讨论lambda时一样, 这里也不会解释curry是如何工作的. 本文中将curry当成黑盒子来对待. 这儿只介绍一下如何用它.
*CURRY -- 咋使*
要使用curry技术, 可以直接在程序中包含"curry"类, 或从它的py文件中导入它. 本程序中, 我们会直接包含它.
原先, 我们认为下面的代码可以将self.buttonHandler和self.button1的command参数绑定起来, 但我们看到它并没有像我们想的那样工作起来.
self.button1 = Button(self.myContainer1,
command = self.buttonHandler(button_name, 1, "Good stuff!"))
利用curry技术, 我们用下面的代码来实现:
self.button1 = Button(self.myContainer1,
command = curry(self.buttonHandler, button_name, 1, "Good stuff!"))
正如你所见, 代码非常清晰. 我们没有调用self.buttonHandler, 而是构造了一个curry对象(它是curry类的一个实例), 将self.buttonHandler作为curry对象的第一个参数. 在这儿, curry对象会记住传给它的函数的名字. 当它被调用时, 它自己会调用之前传给它的那个函数.
*事件绑定*
Chad Netzer已经琢磨出另一种与curry类似的技术, 它可以用来参数化事件绑定. [注意这种编码技术需要python 2或更高版本.] 它用一个"event_lambda"函数来实现.
为了实现curry那样的用法, 你必须将下面这部分"event_lambda"的代码写到你的程序里, 或者写到另一个py文件中再从它导入进来. 本程序是直接把它们写在程序里.
def event_lambda(f, *args, **kwds):
"A helper function that wraps lambda in a prettier interface."
return lambda event, f=f, args=args, kwds=kwds : f(*args, **kwds)
写好这个函数, 我们就可以用它来将self.buttonHandler和事件绑定起来, 代码如下:
self.button1.bind("",
event_lambda(self.buttonHandler, button_name, 1, "Good stuff!"))
如果你对event_lambda是如何工作的非常好奇, 那么看下代码中对button2的绑定代码应该就明白了.
对于button2, 我们用了两步. 首先我们调用event_lambda函数.
event_handler = event_lambda(self.buttonHandler, button_name, 2, "Bad stuff!")
当调完event_lambda函数后, lambda会生成一个新的匿名函数对象.
lambda event, f=f, args=args, kwds=kwds : f(*args, **kwds)
这个匿名函数是对我们真正想调用的函数的一个封装, 在调用event_lambda时, 我们还为这个函数指定了参数.
当event_lambda返回一个匿名函数后, 我们给它赋了一个名字: "event_handler".
event_handler = event_lambda(self.buttonHandler, button_name, 2, "Bad stuff!")
在第二步中, 我们将与"event_handler"绑定起来.
self.button2.bind("", event_handler)
这里注意一下这个匿名函数, 'event'在这儿只是作为一个占位参数. 只有args和kwds才传给button_handler函数.
*我去! 绕死我一堆脑细胞!!*
这只是一个小技巧. 不需要你去费脑子想法明白. 你根本不必为了用"curry"和"event_lambda"而弄明白它们俩是如何工作的. 只它们是俩黑盒子... 用就行了.
*lambda vs. curry vs. event_lambda -- 该用哪个?*
* 用curry和event_lambda相对来说更直接, 简短, 也简单. 缺点是使用它们需要你在代码中包含或导入.
* 相对来说, lambda是python内置的 -- 你不需要再额外做额外做任何事了.
所以用哪个自己选. "你花的钱, 选择权在你." 他们都这么说. 选择你用的最舒服的方式.
这个故事其实是想告诉你...
python是一门非常强大的语言, 它提供了众多创建回调函数的小工具. "Think in Tkinter"只是对这些基本概念作一个简单的介绍, 而不是百科全书, 所以我们在这儿只能讲一点点. 但随着你对python的熟悉, 当你真正需要更加的灵活的时候, python还有很多强大的特性有待你去挖掘.
*代码示例*
程序运行起来后, 它会和前一个程序工作的一样. 这里只是改变了一下实现方式, 执行结果是不变的.
- from Tkinter import *
-
-
# ---------- code for class: curry (begin) ---------------------
-
class curry:
-
"""from Scott David Daniels'recipe
-
"curry -- associating parameters with a function"
-
in the "Python Cookbook"
-
-
"""
-
-
def __init__(self, fun, *args, **kwargs):
-
self.fun = fun
-
self.pending = args[:]
-
self.kwargs = kwargs.copy()
-
-
def __call__(self, *args, **kwargs):
-
if kwargs and self.kwargs:
-
kw = self.kwargs.copy()
-
kw.update(kwargs)
-
else:
-
kw = kwargs or self.kwargs
-
return self.fun(*(self.pending + args), **kw)
-
# ---------- code for class: curry (end) ---------------------
-
-
-
# ---------- code for function: event_lambda (begin) --------
-
def event_lambda(f, *args, **kwds ):
-
"""A helper function that wraps lambda in a prettier interface.
-
Thanks to Chad Netzer for the code."""
-
return lambda event, f=f, args=args, kwds=kwds : f( *args, **kwds )
-
# ---------- code for function: event_lambda (end) -----------
-
-
-
class MyApp:
-
def __init__(self, parent):
-
self.myParent = parent
-
self.myContainer1 = Frame(parent)
-
self.myContainer1.pack()
-
-
button_name = "OK"
-
-
# command binding -- using curry
-
self.button1 = Button(self.myContainer1,
-
command = curry(self.buttonHandler, button_name, 1, "Good stuff!"))
-
-
# event binding -- using the event_lambda helper function
-
self.button1.bind("",
-
event_lambda( self.buttonHandler, button_name, 1, "Good stuff!" ) )
-
-
self.button1.configure(text=button_name, background="green")
-
self.button1.pack(side=LEFT)
-
self.button1.focus_force() # Put keyboard focus on button1
-
-
-
button_name = "Cancel"
-
-
# command binding -- using curry
-
self.button2 = Button(self.myContainer1,
-
command = curry(self.buttonHandler, button_name, 2, "Bad stuff!"))
-
-
# event binding -- using the event_lambda helper function in two steps
-
event_handler = event_lambda( self.buttonHandler, button_name, 2, "Bad stuff!" )
-
self.button2.bind("", event_handler )
-
-
self.button2.configure(text=button_name, background="red")
-
self.button2.pack(side=LEFT)
-
-
-
def buttonHandler(self, argument1, argument2, argument3):
-
print " buttonHandler routine received arguments:", \
-
argument1.ljust(8), argument2, argument3
-
-
def buttonHandler_a(self, event, argument1, argument2, argument3):
-
print "buttonHandler_a received event", event
-
self.buttonHandler(argument1, argument2, argument3)
-
-
print "\n"*100 # clear the screen
-
print "Starting program tt079."
-
root = Tk()
-
myapp = MyApp(root)
-
print "Ready to start executing the event loop."
-
root.mainloop()
-
print "Finished executing the event loop."