# lambda：Python的匿名函数

lambda函数也叫匿名函数，即，函数没有具体的名称。先来看一个最简单例子：

def f(x):
return x**2
print f(4)


Python中使用lambda的话，写成这样

g = lambda x : x**2
print g(4)


lambda表达式在很多编程语言都有对应的实现。比如C#：

var g = x => x**2
Console.WriteLine(g(4))


1. 使用Python写一些执行脚本时，使用lambda可以省去定义函数的过程，让代码更加精简。

2. 对于一些抽象的，不会别的地方再复用的函数，有时候给函数起个名字也是个难题，使用lambda不需要考虑命名的问题。

3. 使用lambda在某些时候让代码更容易理解。

## lambda基础

lambda语句中，冒号前是参数，可以有多个，用逗号隔开，冒号右边的返回值。lambda语句构建的其实是一个函数对象，见证一下：

g = lambda x : x**2
print g

<function <lambda> at 0x00AFAAF0>

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


C#3.0开始，也有了lambda表达式，省去了使用delegate的麻烦写法。C#中的lambda表达式关键字是=>，看下面的一个例子：

var array = new int[] {2, 3, 5, 7, 9};
var result = array.Where(n => n > 3); // [5, 6, 9]


C#使用了扩展方法，才使得数组对象拥有了像Where,Sum之类方便的方法。Python中，也有几个定义好的全局函数方便使用的，他们就是filter, map, reduce。

>>> 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


## 非lambda不可？

print [x * 2 + 10 for x in foo]


print [x for x in foo if x % 3 == 0]


## lambda broken?

fs = [(lambda n: i + n) for i in range(10)]


>>> fs[3](4)
13
>>> fs[4](4)
13
>>> fs[5](4)
13


>>> fs[3](4)
7
>>> fs[4](4)
8
>>> fs[5](4)
9


i = 1
def fs(n):
return n + i
print fs(1) # 2

i = 2
print fs(1) # 3


fs = [(lambda n, i=i : i + n) for i in range(10)]
>>> fs[3](4)
7
>>> fs[4](4)
8
>>> fs[5](4)
9


The following example is one way to compute prime numbers in Python (not the most efficient one, though)

>>> 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]


In the following example, a sentence is split up into a list of words, then a list is created that contains the length of each word.

>>> 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]


I think it doesn’t need any further explanation, the code is practically self-documenting.

Of course, it could all be written in one single statement. Admittedly, this is somewhat less readable (not much, though).

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


Here’s an example from the UNIX scripting world: We want to find all mount points in our file system. To do that, we execute the external “mount” command and parse the output.

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


The getoutput function from the commands module (which is part of the Python standard library) runs the given command and returns its output as a single string. Therefore, we split it up into separate lines first. Finally we use “map” with a lambda function that splits each line (on whitespace, which is the default) and returns just the third element of the result, which is the mountpoint.

Again, we could write all of that in one single statement, which increases compactness but reduces readability:

When writing “real-world” scripts, it is recommended to split up complex statements so that it is easier to see what it does. Also, it is easier to make changes.

However, the task of splitting up the output of a command into a list of lines is very common. You need it all the time when parsing the output of external commands. Therefore, it is common practice to include the split operation on the getoutput line, but do the rest separately. This is a good trade-off between compactness and readability:

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


An even better idea is probably to write a small function for that task, which encapsulates the job of running the command and splitting the output.

On a related note, you can also use so-called list comprehensions to construct lists from other lists. Sometimes this is preferable because of efficiency or readability. The previous example could very well be rewritten using a list comprehension:

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


In many cases, you can use list comprehensions instead of map() or filter(). It depends on the situation which one should be preferred.

Note: The commands module is deprecated in newer versions of Python (though it still works in all 2.x versions). Instead, the subprocess module should be used which is available since Python 2.4. The commands.getoutput() function can be replaced by subprocess.check_output(). Please refer to the documentation for details.

• 评论

• 上一篇
• 下一篇