lambda与闭包
2010-05-03 16:43 FantasySoft 阅读(7339) 评论(6) 编辑 收藏 举报在《作为.NET程序员,您需要IronPython么?》一文中,Michael给大家介绍了IronPython的诸多优点,其中的一条就是IronPython实现闭包要比C#和VB.NET来得更为直观。在文中,针对“找出长度较短的字符串”的功能,我给出了IronPython的代码:
>>> maxLength = raw_input("Maximum length of string to include?")
>>> maxLength = int(maxLength)
>>> shortWords = [i for i in SampleData if len(i) <= maxLength]
这段代码的核心在于最后一个赋值语句,它是典型的列表内涵(List Comprehension)的应用。而事实上,这个赋值语句等价于以下的代码:
使用这样的代码也许和C#和Java的闭包-Jon谈《The Beauty of Closures》一文中C#代码具有更强的可比性,因为lambda是Python中用于定义匿名函数的关键字,而匿名函数又是实现闭包的充分条件之一(非必要条件)。在讨论闭包和lambda之前,我们先来了解一下filter函数。
filter(function, sequence)函数是Python的内建函数,我们无需引入任何的包(package)即可使用。它实现了将序列中某些符合条件的元素筛选出来的功能;它需要两个参数:第一个是函数,用于定义筛选条件;而另一个则是序列,作为被筛选的对象。Python Tutorial给出的示例代码可以帮助我们更好地理解filter这个函数。
现在,您对filter函数有一定的认识了吧?那么让我们再回到上述的赋值语句。lambda定义了一个匿名函数,它返回了一个布尔值:True或者False,而判断条件则是:参数的长度小于或者等于maxLenth。在这里,lambda充其量只是一个语法糖,让代码更加易读,并没有体现出它与闭包的关系。说到这里,您一定会问:到底什么是闭包呢?从定义上来说:闭包就是由函数与其相关的引用环境组合而成的实体。如果一门语言很好地支持闭包,那么它需要具备以下条件:
• 函数是语言的一等公民,它不从属于其它任何对象,可以作为方法的返回值;
• 函数可以嵌套定义;
• 可以捕获引用环境,并把引用环境和函数代码组成一个可调用的实体;
• 允许定义匿名函数。
基于上述条件的第三点,我们可以将上述代码改写为如下形式:
>>> def filterFunc(maxLength):
... return lambda i: len(i) <= maxLength
>>> maxLength = raw_input("Maximum length of string to include?")
>>> maxLength = int(maxLength)
>>> shortWords = filter(filterFunc(maxLength), SampleData)
我们回头对照一下上述的条件,filterFunc不正是闭包的最好体现么?也许您会觉得我有点画蛇添足,多此一举,但是,我们确实拥有了一个极具灵活性的可调用实体。如果要筛选出最大长度为4的单词,我们可以这么做:
如果要筛选出最大长度为3的单词,我们又可以这么做:
我们回想一下面向对象编程当中的函数,它的定义是固定的,不会在运行时发生变化。而闭包则不同,它会根据不同的引用环境发生变化,从而返回不同的函数实例。因此,我会把闭包等同于返回值是函数的函数,而lambda的存在可以让返回的函数定义得到简化。更多有关闭包的介绍,请参考园子里的季方亮同学的文章——《函数编程之闭包漫谈(Closure)》;有关lambda的介绍,则请参考CoderZh同学的文章——《Python天天美味(35) - 细品lambda》。
最后,也许有朋友会问:我们为什么需要lambda和闭包呢?这是为了将side-effect降至最低,也就是将变量从我们代码当中剔除!我想您一定会对此很惊讶,但这是事实,最纯粹的函数式编程是只有定义的。基于此,最开始的代码还能演变成如下所示:
>>> raw_input("Maximum length of string to include?")
>>> print(filter(lambda i:len(i) <= int(_), SampleData))
对编程范式感兴趣的朋友,可以参考我的文章:《说的都是概念——有关编程范式》。