Python 进阶系列1:lambda函数与高阶函数

.

Python作为一门流行的编程语言,广受程序员与数据科学家的喜爱。如今的Python拥有丰富的第三方库和良好的生态发展,使其能够在数据科学、人工智能、web等各领域获得广泛的支持。Python进阶系列旨在分享一些Python的高级特性,如Lambda表达式、元类(metaclass)的使用、类(class)和类型(type)的统一、经典类与新式类区别与应用等。

作者:DjangoPeng

lambda函数

在计算机编程中,匿名函数(anonymous function)是指一类无需定义标识符(函数名)的函数子程序,普遍存在于多种编程语言中。1958年LISP首先采用匿名函数,自此之后,越来越多编程语言陆续采用,如C++、Java、Python。

lambda作为一个关键字,用来实现Python的匿名函数,与def定义的普通函数相比较而言,lambda是单一的表达式,而不是代码块(block)。我们只能在lambda函数中封装有限的逻辑(通常就是一个表达式),这样做的目的是为了引导大家用lambda实现功能简单的函数,而复杂的逻辑仍然交由def处理。

定义

Python支持在运行时动态创建Lambda函数(匿名函数)。

语法:

1
lambda arg1, arg2,...,argN : an expression with args

以”:”作为分割,前为函数入参,后为整个函数的表达式,计算结果就作为函数的返回结果。

定义求一个数的平方的函数

lambda函数

1
>>> lambda x : x**2

def函数

1
2
>>> def pow(x):
... return x**2

通过对比,可以发现lambda函数的语法更加简练,无需显示”return”,而是直接返回表达式计算结果。

调用方式

方式1:lambda函数作为一个函数对象,同样可以将其赋值给变量,然后就像普通函数一样通过func()形式进行调用:

1
2
3
>>> pow = lambda x, x**2
>>> result = pow(2)
4

方式2:直接在lambda函数后加上一对小括号进行调用:

1
2
>>>(lambda x : x**2)(2)
4

高阶函数

当一个函数接受另一个函数对象作为入参时,这种函数就称为高阶函数(Higher-order function)。

定义

定义一个高阶函数用于求两个数的和,同时接受一个函数对这两个数进行预处理,如:绝对值、开方、平方等。

1
2
>>> def myAdd(a, b, f):
... return f(a) + f(b)

其中a,b是用于求和的两个参数,f是对其进行预处理的函数。不妨先令f=abs,则有:

1
2
>>> myAdd(1, -1, abs)
2

我们之前定义了求平方的函数pow:

1
2
3
4
>>> def pow(x):
... return x**2
>>> myAdd(2, -2, pow)
8

正如之前所言,pow函数只有一个简单的表达式,完全可以由lambda函数代替:

1
2
>>> myAdd(2, -2, lambda x : x**2)
8

实例

下面实例中的map、reduce、filter都是Python内置的高阶函数,

1
2
3
4
5
6
7
8
9
# 求[0,1,2,3,4,5,6,7,8,9]各自的平方
>>> map(lambda x : x**2, xrange(10))
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# 求0~99的和
>>> reduce(lambda x, y: x + y, xrange(100))
4950
# 求0~10中的偶数
>>> filter(lambda x: x % 2==0, xrange(10))
[0, 2, 4, 6, 8]

如果用def定义普通函数来完成上面的工作,以求和为例,则显得过于冗长:

1
2
3
4
5
>>> def sum(a,b):
... return a + b
...
>>> reduce(sum, xrange(100))
4950

数字字符串转换

输入一个字符串数字,将其转换为整型数字,通过lambda将其封装成str2int函数:

1
2
3
4
5
6
>>> def str2int(s):
... return reduce(lambda x, y: int(x)*10 + int(y), s)
...
>>> num = str2int('12345')
>>> print(num)
12345

嵌套排序问题

当实现各种特定的排序需求时,如dict按value排序、list嵌套dict排序等,如果结合sorted()函数和lambda来处理,往往能达到事半功倍的效果。

sorted()函数原型:

1
sorted(iterable[,key][,reverse])

iterable是需要排序的变量,key是指定排序的元素,reverse指定是否逆序。

问题1:dict按key排序

1
2
3
4
5
>>> dict = {'a':3, 'b': 1, 'c':2}
>>> sorted(dict)
['a', 'b', 'c']
>>> sorted(dict, reverse=True)
['c', 'b', 'a']

问题2:dict按value排序

1
2
3
4
5
>>> dict = {'a':3, 'b': 1, 'c':2}
>>> sorted(dict, key=lambda k: dict[k])
['b', 'c', 'a']
>>> sorted(dict, key=lambda k: dict[k], reverse=True)
['a', 'c', 'b']

问题3:list嵌套list排序

1
2
3
4
5
6
7
>>> list = [[7, 4, 1], [2, 8, 5], [6, 9, 3]]
>>> sorted(list, key=lambda k: k[0]) #按各列表第0个升序
[[2, 8, 5], [6, 9, 3], [7, 4, 1]]
>>> sorted(list, key=lambda k: k[0], reverse=True) #按各列表第0个降序
[[7, 4, 1], [6, 9, 3], [2, 8, 5]]
>>> sorted(list, key=lambda k: k[1]) #按各列表第1个升序
[[7, 4, 1], [2, 8, 5], [6, 9, 3]]

问题4:list嵌套dict排序

1
2
3
4
5
6
7
8
9
10
11
>>> list = [
... {'x': 3, 'y': 2, 'z': 1},
... {'x': 2, 'y': 1, 'z': 3},
... {'x': 1, 'y': 3, 'z': 2},
... ]
>>> sorted(list, key=lambda k: k['x']) #按dict中x的value升序
[{'y': 3, 'x': 1, 'z': 2}, {'y': 1, 'x': 2, 'z': 3}, {'y': 2, 'x': 3, 'z': 1}]
>>> sorted(lis, key=lambda k: k['x'], reverse=True) #按dict中x的value降序
[{'y': 2, 'x': 3, 'z': 1}, {'y': 1, 'x': 2, 'z': 3}, {'y': 3, 'x': 1, 'z': 2}]
>>> sorted(list, key=lambda k: k['y']) #按dict中y的value升序
[{'y': 1, 'x': 2, 'z': 3}, {'y': 2, 'x': 3, 'z': 1}, {'y': 3, 'x': 1, 'z': 2}]

问题5:dict嵌套list排序

1
2
3
4
5
6
7
8
9
10
11
>>> dict = {
... 'a': [1, 2, 3],
... 'b': [2, 1, 3],
... 'c': [3, 1, 2],
... }
>>> sorted(dict, key=lambda k: dict[k][0])
['a', 'b', 'c']
>>> sorted(dict, key=lambda k: dict[k][0], reverse=True)
['c', 'b', 'a']
>>> sorted(dict, key=lambda k: dict[k][1])
['c', 'b', 'a']

总结

在实现简单功能的函数时,为了提升代码可读性,减少函数定义,可以尝试使用lambda编写匿名函数。其好处是显而易见的,当其与常用的高阶函数如map、reduce、fileter、sorted等结合时,能够产生事半功倍的效果。