Python函数式编程-map()、zip()、filter()、reduce()、lambda()

时间:2021-08-07 18:39:20

三个函数比较类似,都是应用于序列的内置函数。常见的序列包括list、tuple、str


map函数


map函数会根据提供的函数对指定序列做映射。

map函数的定义:

map(function, sequence[, sequence, ...]) -> list

map()函数接收两个参数,一个是函数,一个是序列,map将传入的函数依次作用到序列的每个元素,并把结果作为新的list返回。


1、当seq只有一个时,将函数func作用于这个seq的每个元素上,并得到一个新的seq。
让我们来看一下只有一个seq的时候,map()函数是如何工作的。

Python函数式编程-map()、zip()、filter()、reduce()、lambda()

示例一

比如要对一个序列中的每个元素进行平方运算:

map(lambda x: x ** 2, [1, 2, 3, 4, 5])

返回结果为:

[1, 4, 9, 16, 25]

或者

>>> def f(x):
... return x * x
...
>>> map(f, [1, 2, 3, 4, 5])
[1, 4, 9, 16, 25]

map()传入的第一个参数是f,即函数对象本身。

注意:map()函数不改变原有的 list,而是返回一个新的 list。

不需要map()函数,写一个循环,也可以计算出结果

L = []
for n in [1, 2, 3, 4, 5]:
L.append(f(n))
print L

把f(x)作用在list的每一个元素并把结果生成一个新的list。

示例二

#使用lambda
>>> print map(lambda x: x % 2, range(7))
[0, 1, 0, 1, 0, 1, 0]
#使用列表解析
>>> print [x % 2 for x in range(7)]
[0, 1, 0, 1, 0, 1, 0]

2、当seq多于一个时,map可以并行(注意是并行)地对每个seq执行如下图所示的过程

Python函数式编程-map()、zip()、filter()、reduce()、lambda()

在参数存在多个序列时,会依次以每个序列中相同位置的元素做参数调用function函数。

示例

比如要对两个序列中的元素依次求和。

map(lambda x, y: x + y, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10])

map返回的list中第一个元素为,参数序列1的第一个元素加参数序列2中的第一个元素(1 + 2),
list中的第二个元素为,参数序列1中的第二个元素加参数序列2中的第二个元素(3 + 4),
依次类推,最后的返回结果为:

[3, 7, 11, 15, 19]

要注意function函数的参数数量,要和map中提供的集合数量相匹配。
如果集合长度不相等,会以最小长度对所有集合进行截取。


当函数为None时,操作和zip相似

map(None, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10])

返回结果为:

[(1, 2), (3, 4), (5, 6), (7, 8), (9, 10)]


map()作为高阶函数,事实上它把运算规则抽象了,因此,我们不但可以计算简单的f(x)=x2,还可以计算任意复杂的函数,比如,把这个list所有数字转为字符串

>>> map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9])
['1', '2', '3', '4', '5', '6', '7', '8', '9']

由于list包含的元素可以是任何类型,因此,map() 不仅仅可以处理只包含数值的 list,事实上它可以处理包含任意类型的 list,只要传入的函数f可以处理这种数据类型。

比如假设用户输入的英文名字不规范,没有按照首字母大写,后续字母小写的规则,请利用map()函数,把一个list(包含若干不规范的英文名字)变成一个包含规范英文名字的list:
输入:[‘adam’, ‘LISA’, ‘barT’]
输出:[‘Adam’, ‘Lisa’, ‘Bart’]

def format_name(s):
s1=s[0:1].upper()+s[1:].lower();
return s1;

print map(format_name, ['adam', 'LISA', 'barT'])
***将元组转换成list***
>>> map(int, (1,2,3))
[1, 2, 3]
***将字符串转换成list***
>>> map(int, '1234')
[1, 2, 3, 4]
***提取字典的key,并将结果存放在一个list中***
>>> map(int, {1:2,2:3,3:4})
[1, 2, 3]
***字符串转换成元组,并将结果以列表的形式返回***
>>> map(tuple, 'agdf')
[('a',), ('g',), ('d',), ('f',)]
#将小写转成大写
def u_to_l (s):
return s.upper()
print map(u_to_l,'asdfd')

zip函数


Python函数式编程之zip()
zip()函数具体的工作机制是,将每个列表中同一位置的元素取出来组成一个元组,存放到一个列表中,然后返回这个列表。

>>> x = [1,2,3]
>>> y = ['a','b','c']
>>> z = [4,5,6]
>>> zip_xyz = zip(x, y, z)
>>> print zip_xyz
[(1, 'a', 4), (2, 'b', 5), (3, 'c', 6)]

对于长度不同的seq,zip()函数又怎么处理呢?

>>> a = [1,2,3]
>>> b = [4,5,6,7]
>>> zip_ab = zip(a, b)
>>> print zip_ab
[(1, 4), (2, 5), (3, 6)]

从上面的例子可以看出,当seq的长度不一致时,zip()会以最短的那个seq为主,进行处理,然后将多余的舍弃掉。

zip()对只有一个seq的处理:

>>> c = ['a', 'b', 'c']
>>> zip_c = zip(c)
>>> print zip_c
[('a',), ('b',), ('c',)]

unzip

>>> a = [1, 2, 3]
>>> b = ['a', 'b', 'c']
>>> zip_ab = zip(a,b)
>>> un_zip = zip(*zip_ab)
>>> print un_zip
[(1, 2, 3), ('a', 'b', 'c')]

一般认为这是一个unzip过程,工作原理如下:
在运行zip(*zip_ab)之前,zip_ab的值是:[(1, ‘a’), (2, ‘b’), (3, ‘c’)]
而zip(*zip_ab)就等价于zip((1, ‘a’), (2, ‘b’), (3, ‘c’))
所以得出结果:[(1, 2, 3), (‘a’, ‘b’, ‘c’)]

>>> x = [1,'a','b']
>>> zip_rx = zip(* [x] * 3)
>>> print zip_rx
[(1, 1, 1), ('a', 'a', 'a'), ('b', 'b', 'b')]

首先 [x]生成一个列表的列表:[[1, ‘a’, ‘b’]],这个列表中只有一个元素[1,’a’,’b’]
其次[x] * 3,表示将列表中的元素打印三次,即生成了含有三个元素的列表:
[[1, ‘a’, ‘b’], [1, ‘a’, ‘b’], [1, ‘a’, ‘b’]]
最后是zip(* [x] * 3)就等价于
zip([1, ‘a’, ‘b’], [1, ‘a’, ‘b’], [1, ‘a’, ‘b’])


filter函数


filter函数会对指定序列执行过滤操作。
filter函数的定义:

filter(function or None, sequence) -> list, tuple, or string

filter函数会对序列参数sequence中的每个元素调用function函数,最后返回的结果包含调用结果为True的元素序列。返回值的类型和参数sequence的类型相同. func函数是一个布尔函数,filter()函数调用这个函数一次作用于seq中的每一个元素,筛选出符合条件的元素,并以列表的形式返回。

示例一

比如返回序列中的所有偶数:

def is_even(x):
return x & 1 != 0

filter(is_even, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])

返回结果为:
[1, 3, 5, 7, 9]
如果function参数为None,返回结果和sequence参数相同。

示例二

假如有个列表,列表中有几个数字,现在我想从这些数字中,选出即能被2整除又能被3整除的数。

nums = [2,3,6,12,15,18]
def nums_res (x):
return x % 2 == 0 and x % 3 == 0
print filter(nums_res, nums)
执行结果:[6, 12, 18]

如果使用普通方法的话,就需要使用for循环去挨个挨个遍历list中的元素。当然我们也可以使用列表解析法:

>>> print [x for x in nums if x % 2 == 0 and x % 3 == 0]
[6, 12, 18]

reduce函数


reduce函数,即为化简函数,reduce函数会对参数序列中元素进行累积。
reduce函数的定义:

reduce(function, sequence[, initial]) -> value

function参数是一个有两个参数的函数,reduce依次从sequence中取一个元素,和上一次调用function的结果做参数再次调用function。
第一次调用function时,如果提供initial参数,会以sequence中的第一个元素和initial作为参数调用function,否则会以序列sequence中的前两个元素做参数调用function。

reduce()函数的执行过程如下图所示

Python函数式编程-map()、zip()、filter()、reduce()、lambda()

reduce把一个函数作用在一个序列[x1, x2, x3…]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

示例一

比方说对一个序列求和,就可以用reduce实现

>>> def add(x, y):
... return x + y
...
>>> reduce(add, [1, 3, 5, 7, 9])
25

或者

reduce(lambda x, y: x + y, [1, 3, 5, 7, 9])

结果为25

或者

reduce(lambda x, y: x + y, [3, 5, 7, 9], 1)

结果为25( ((((1+3)+5)+7)+9) )

当然求和运算可以直接用Python内建函数sum(),没必要动用reduce。

示例二

从reduce函数的执行过程,很容易联想到求一个数的阶乘,而python中并没有给出一个求阶乘的内置函数,正好就拿这个例子来说明reduce函数。

#未指定init的情况
>>> n = 6
>>> print reduce(lambda x, y: x * y, range(1, n))
120

上面的例子中range(1,6)函数生成的是一个[1, 2, 3, 4, 5]这样的列表,这里我们给它个名叫seq1吧,reduce()函数执行时,由于没有指定init参数,所以将取seq1中的第一个元素1,作为第一个元素,由于前面的lambda有2个变量,所以需要两个实参,于是就取seq1中的第2个元素2,与第一个元素1一起传入lambda中去执行,并将返回结果2,并同下一个元素3再一起传入lambda中执行,再次返回的结果,作为下一次执行的第一个元素,依次类推,就得出结果5! = 120。

如果我们希望得到阶乘的结果再多增加几倍,可以启用init这个可选项。如:

>>> print reduce(lambda x, y: x * y, range(1, n),2)
240

这个时候,就会将init作为第一个元素,和seq1中的第一个元素1一起传入lambda函数中去执行,返回结果再作为下一次的第一个元素。


但是如果要把序列[1, 3, 5, 7, 9]变换成整数13579,reduce就可以派上用场

>>> def fn(x, y):
... return x * 10 + y
...
>>> reduce(fn, [1, 3, 5, 7, 9])
13579

这个例子本身没多大用处,但是,如果考虑到字符串str也是一个序列,对上面的例子稍加改动,配合map(),我们就可以写出把str转换为int的函数:

>>> def fn(x, y):
... return x * 10 + y
...
>>> def char2num(s):
... return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
...
>>> reduce(fn, map(char2num, '13579'))
13579

整理成一个str2int的函数就是:

def str2int(s):
def fn(x, y):
return x * 10 + y
def char2num(s):
return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
return reduce(fn, map(char2num, s))

还可以用lambda函数进一步简化成

def char2num(s):
return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
def str2int(s):
return reduce(lambda x,y: x*10+y, map(char2num, s))

也就是说,假设Python没有提供int()函数,你完全可以自己写一个把字符串转化为整数的函数,而且只需要几行代码

注意function函数不能为None。


lambda函数


Python中,lambda函数也叫匿名函数,及即没有具体名称的函数,它允许快速定义单行函数,类似于C语言的宏,可以用在任何需要函数的地方。这区别于def定义的函数。
lambda与def的区别:

1)def创建的方法是有名称的,而lambda没有。
2)lambda会返回一个函数对象,但这个对象不会赋给一个标识符,而def则会把函数对象赋值给一个变量(函数名)。
3)lambda只是一个表达式,而def则是一个语句。
4)lambda表达式” : “后面,只能有一个表达式,def则可以有多个。
5)像if或for或print等语句不能用于lambda中,def可以。
6)lambda一般用来定义简单的函数,而def可以定义复杂的函数。
6)lambda函数不能共享给别的程序调用,def可以。

lambda语法格式:
lambda 变量 : 要执行的语句

lambda [arg1 [, agr2,…..argn]] : expression

1、单个参数的:
>>> g = lambda x : x ** 2
>>> print g(3)
9
2、多个参数的:
>>> g = lambda x, y, z : (x + y) ** z
>>> print g(1,2,2)
9

lambda表达式会返回一个函数对象,如果没有变量接受这个返回值的话,它很快就会被丢弃。也正是由于lambda只是一个表达式,所以它可以直接作为list和dict的成员。如:

>>> list_a = [lambda a: a**3, lambda b: b**3]
>>> list_a[0]
<function <lambda> at 0x0259B8B0>
>>> g = list_a[0]
>>> g(2)
8

lambda表达式中,冒号前面是参数,可以有多个,用逗号分隔,冒号右边是返回值。


参考文献


python中的map、filter、reduce函数

python map函数

Python map()函数的用法

Python reduce()函数的用法

Python filter()函数的用法

Python lambda函数的用法