Chinaunix首页 | 论坛 | 博客
  • 博客访问: 71986
  • 博文数量: 26
  • 博客积分: 628
  • 博客等级: 中士
  • 技术积分: 315
  • 用 户 组: 普通用户
  • 注册时间: 2008-04-22 11:57
文章分类

全部博文(26)

文章存档

2012年(4)

2011年(19)

2010年(3)

我的朋友

分类: Python/Ruby

2011-04-28 18:06:39

前一个程序中, 我们介绍了一下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还有很多强大的特性有待你去挖掘.

*代码示例*

程序运行起来后, 它会和前一个程序工作的一样. 这里只是改变了一下实现方式, 执行结果是不变的.

  1. from Tkinter import *

  2. # ---------- code for class: curry (begin) ---------------------
  3. class curry:
  4.     """from Scott David Daniels'recipe
  5.     "curry -- associating parameters with a function"
  6.     in the "Python Cookbook"
  7.     
  8.     """

  9.     def __init__(self, fun, *args, **kwargs):
  10.         self.fun = fun
  11.         self.pending = args[:]
  12.         self.kwargs = kwargs.copy()

  13.     def __call__(self, *args, **kwargs):
  14.         if kwargs and self.kwargs:
  15.             kw = self.kwargs.copy()
  16.             kw.update(kwargs)
  17.         else:
  18.             kw = kwargs or self.kwargs
  19.         return self.fun(*(self.pending + args), **kw)
  20. # ---------- code for class: curry (end) ---------------------


  21. # ---------- code for function: event_lambda (begin) --------
  22. def event_lambda(f, *args, **kwds ):
  23.     """A helper function that wraps lambda in a prettier interface.
  24.     Thanks to Chad Netzer for the code."""
  25.     return lambda event, f=f, args=args, kwds=kwds : f( *args, **kwds )
  26. # ---------- code for function: event_lambda (end) -----------
  27.         
  28.         
  29. class MyApp:
  30.     def __init__(self, parent):
  31.         self.myParent = parent
  32.         self.myContainer1 = Frame(parent)
  33.         self.myContainer1.pack()
  34.         
  35.         button_name = "OK"
  36.         
  37.         # command binding -- using curry
  38.         self.button1 = Button(self.myContainer1,
  39.          command = curry(self.buttonHandler, button_name, 1, "Good stuff!"))

  40.         # event binding -- using the event_lambda helper function
  41.         self.button1.bind("",
  42.             event_lambda( self.buttonHandler, button_name, 1, "Good stuff!" ) )
  43.                  
  44.         self.button1.configure(text=button_name, background="green")
  45.         self.button1.pack(side=LEFT)
  46.         self.button1.focus_force() # Put keyboard focus on button1
  47.         
  48.         
  49.         button_name = "Cancel"
  50.         
  51.         # command binding -- using curry
  52.         self.button2 = Button(self.myContainer1,
  53.             command = curry(self.buttonHandler, button_name, 2, "Bad stuff!"))
  54.             
  55.         # event binding -- using the event_lambda helper function in two steps
  56.         event_handler = event_lambda( self.buttonHandler, button_name, 2, "Bad stuff!" )
  57.         self.button2.bind("", event_handler )
  58.         
  59.         self.button2.configure(text=button_name, background="red")
  60.         self.button2.pack(side=LEFT)

  61.     
  62.     def buttonHandler(self, argument1, argument2, argument3):
  63.         print " buttonHandler routine received arguments:", \
  64.             argument1.ljust(8), argument2, argument3
  65.         
  66.     def buttonHandler_a(self, event, argument1, argument2, argument3):
  67.         print "buttonHandler_a received event", event
  68.         self.buttonHandler(argument1, argument2, argument3)
  69.         
  70. print "\n"*100 # clear the screen
  71. print "Starting program tt079."                            
  72. root = Tk()
  73. myapp = MyApp(root)
  74. print "Ready to start executing the event loop."
  75. root.mainloop()
  76. print "Finished executing the event loop."
阅读(840) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~