(译)Python: Lambda Functions(lambda函数)

http://www.secnetix.de/olli/Python/lambda_functions.hawk


python:Lambda Functions

Python:lambda函数


注意:每一行以”>>>”开始,”...”表示换行继续上一行输入。

python运行时通过一个叫”lambda”的函数来实现大量的匿名函数(即一个没有函数名的函数)
python中的lambda与函数式编程语言里边的lambda的有些区别,这是python中一个很有用的概念,它常被用来实现一些常用函数,例如filter(),map()和reduce()。

下边的代码说明了常规函数与lambda的差别:

>>> def f (x): return x**2
... 
>>> print f(8)
64
>>> 
>>> g = lambda x: x**2
>>>
>>> print g(8)
64


     像你看到的,f()和g()在做同样的事情,并且调用方法一样。注意lambda定义不包含”return”这个语句,它一般会包含一个返回值的表达式。并且你可以将lambda放到任意你想放到的地方,并且你根本不用给他赋变量。

     接下来的代码片段展示了lambda函数的用法,这个用法要在2.2以后的版才能实现,因为这个需要支持嵌套作用域(nested scopes)

>>> def make_incrementor (n): return lambda x: x + n
>>>
>>> f = make_incrementor(2)
>>> 
>>> print f(42), g(42)
44 48
>>> 
>>> print make_incrementor(22)(33)
55


   上边的代码定义”make_incrementor”创建了一个匿名函数,并且返回它。返回的函数使它可以在创建时指定特定的函数体内的一些运行参数。

你可以创建镀铬不同的增量函数然后赋予它们多个不同变量,然后独立地使用他们。像最后一个语句展示的那样,你甚至不需要在任何地方赋予一个函数名,你可以直接用他们,而当再也不需要它们的时候扔掉它们。

====

下边的会介绍更深层次的东西。

>>> foo = [2, 18, 9, 22, 17, 24, 8, 12, 27]
>>> 
>>> print filter(lambda x: x % 3 == 0, foo)
[18, 9, 24, 12, 27]
>>> 
>>> print map(lambda x: x * 2 + 10, foo)
[14, 46, 28, 54, 44, 58, 26, 34, 64]
>>> 
>>> print reduce(lambda x, y: x + y, foo)
139


首先,我们定义一个整形list,然后我们用标准函数filter(),map(),reduce()在这个list上执行。这3个函数要两个参数:一个函数和一个list。

当然,我们可以在别的地方定义一个函数,之后用那个函数名来作为filter()的参数等,事实上,当一个函数会用到很多次或一行写不下的时候我们可能会这样做。但是当我们只用某个函数一次,并且参数很少时,使用lambda来创建一个(暂时)的匿名函数然后直接传入filter()是一个很好的选择。这样能写出紧凑,可读性好的代码。


在上一个例子里,filter()调用lambda函数为每个list中的元素,然后返回一个新的list,这个list里的数值是函数返回”True”,在上个例子里,我们得到的一些可以被3整除的数字。表达式x%3 == 0计算出x对3求余以后与1的关系(当x被3整除的时候返回”True”)。

在上边例子中第二函数map()那,map()被用来转化我们的list给定的函数调用了每个元list中的元素,之后一个新的list被创建,这个新的list包含lambda中返回的值,在这个例子里,它计算2 * x + 10。

最后,reduce()更加特殊,”worker function”要传入两个参数(我们称之为x,y)(译者注:python3.0里多个变量lambda语法声明格式已经改变,具体去搜一下,不影响使用)。函数调用了list里的前两个变量,然后用得到的结果与第三个变量再一起传入函数中,一直那前边得到的结果与下一个list中元素一起传入函数中,直到所有的元素都被传过一遍。这样,如果list中n个元素,我们的函数就被调用了n – 1次。返回的最后的值就是reduce()的返回值。再上边的例子中,很容易地添加变量,所以我们得到了list中所有元素的和。


====

下边的例子会用python计算出素数。(虽然不是最高效的)

>>> nums = range(2, 50) 
>>> for i in range(2, 8): 
...     nums = filter(lambda x: x == i or x % i, nums)
... 
>>> print nums
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]


这个程序怎么工作?首先,我们将2到49放入”nums”这个list中。之后我们”for”了一遍所有可能的被除数,(即”i”的值从2到7)自然地,所有的可以被某一个数整除的不是素数。所以我们就用一个过滤(filter())函数来将他们从list中移除。(这个算法叫做”the sieve of Eratosthenes”(Eratosthenes筛法))。


在上边的例子中,filter函数只是说:“如果有元素与i相等或除i以后有余数,就将这些元素留下,其他的都从list中移除!”(素数是仅能能被1和本身整除的数)

在过滤器(fliter)执行完以后,只有素数被留在了list中。我不介意你用另一个语言来实现这个如此紧凑的并且可读性高的代码来实现。(除了函数式语言)


注意:range(x, y)只是返回一个包含从x到y-1的list,例如range(5, 10)返回[5, 6, 7, 8, 9]。


====

在下边的例子中,一个例子会将单词分出来,之后创建一个list包含所有单词,最后创建一个list包含所有单词的长度。

>>> sentence = 'It is raining cats and dogs'
>>> words = sentence.split()
>>> print words
['It', 'is', 'raining', 'cats', 'and', 'dogs']
>>> 
>>> lengths = map(lambda word: len(word), words)
>>> print lengths
[2, 2, 7, 4, 3, 4]


我认为这不需要跟多的解释了,代码很实际,并且自己说明了自己的用途。


当然,这些可以以被写在一行里,但是这样,代码就有一写不好读懂(虽然不是非常难读懂)

>>> print map(lambda w: len(w), 'It is raining cats and dogs'.split())
[2, 2, 7, 4, 3, 4]


====

之后是一个UNIX的脚本的例子:我们想找到文件系统中的所有的挂载点,我们执行外部命令”mount“,并且输出结果。

>>> import commands
>>> 
>>> mount = commands.getoutput('mount -v')
>>> lines = mount.split('\n')
>>> points = map(lambda line: line.split()[2], lines)
>>> 
>>> print points
['/', '/var', '/usr', '/usr/local', '/tmp', '/proc']


包含在“commands”(python标准库函数)里的“getoutput”函数运行传入的命令,之后返回一个string,包含命令的执行结果。所以,我们用行来将结果分开。最后我们用lambda函数和“map”来分割每一行(split()默认用空格分)然后返回第三个结果,这个结果就是我们想得到的挂载点。

再一次,我们可以将这些语句写在一个句子中,这将会提高紧凑程度,但降低可读性。

>>>print map(lambda x: x.split()[2], commands.getoutput('mount -v').split('\n'))
['/', '/var', '/usr', '/usr/local', '/tmp', '/proc']


当我们写“现实世界”的脚本的时候,最好的选择是将复杂的语句分开,使它们很容易就看出它要做什么,同时,这将会使它更易于修改。

但是,将输出分成行很常见,你总是需要将输出分成一行一行的。所以,将split()操作放在output后边比较常见,但是其他的操作再单写一行。

>>> lines = commands.getoutput('mount -v').split('\n')
>>> 
>>> points = map(lambda line: line.split()[2], lines)
>>> print points
['/', '/var', '/usr', '/usr/local', '/tmp', '/proc']


还有一种比较好的方法就是为这个程序写小函数,封装命令并且分开输出。

在一个有关的笔记中,你可以用所谓的list表达式来从其他的list中够造一个list,有时候,这个很好,因为它很搞笑并且可读性好,后边的例子会用一个list表达式来写出。

>>> lines = commands.getoutput('mount -v').split('\n')
>>> 
>>> points = [line.split()[2] for line in lines]
>>> print points
['/', '/var', '/usr', '/usr/local', '/tmp', '/proc']


在许多情况下,你可以用list表达式而不是map()或filter()。是否用要由你使用的情况来考虑。

你可能感兴趣的:(编程,算法,python,F#,脚本)