本篇文章为大家带来Python一些内建的函数式风格的函数,熟练利用这些函数可以令你的程序更加简洁明晰。
MapReduce
所谓map-reduce处理模式,最早由谷歌一篇论文中提出(参考文献[1])。map指将一个序列的每个元素按照某一方式映射为一个新的序列的元素,而reduce则是将一个序列按照某种方式进行归纳,得出一个简单的数值结果。Python自身支持操作。对于操作,龟叔从Python 3起将它移到了标准库里(龟叔是谁?→
传送门
)。一个主要原因是龟叔认为操作通常不能让人一眼看出来它到底要计算什么,还需要拿纸笔画一画写一写。与其这样,不如显式指明究竟要算什么东西。下面分别来看一下和的用法。将一个可迭代对象映射为一个新可迭代对象,它接收两个参数,前一个是映射方式,后一个是源可迭代对象。自然,映射方式需要一个接收一个参数的函数:
l= [xforxinrange(5)]
print(l)
# [0, 1, 2, 3, 4]
defcompute(x):
returnx+1
l2=map(compute,l)
print(l2)
#
返回的是一个对象,它是一个迭代器(什么是迭代器?→传送门)。
print(check_iterator(l2))
# True
# 看一下l2的值
print(list(l2))
# [1, 2, 3, 4, 5]
当然你可以用表达式来定义函数:
l3=map(lambdax:x**x,l)
print(list(l3))
# [1, 1, 4, 27, 256]
map所对应的推导式写法是:
l3= [x**xforxinl]
print(list(l3))
# [1, 1, 4, 27, 256]
哪一个更简洁可以自行评判。是一个归纳的过程,它的第一个参数要求一个接收两个参数的函数,第二个参数同样为一个可迭代对象:
fromfunctoolsimportreduce
l4=reduce(lambdax,y:x+y,l)
print(l4)
# 10
从这里我们大致窥见的原理。它依顺序将中两个元素做函数指定的操作,并将操作的结果作为下一次计算的一个元素,再继续从列表中读取下一个元素,直至列表结束。上例中做了这样的事:。所以,用可以很方便得实现阶乘操作:
deffactorial(x):
returnreduce(
lambdax,y:\
x*y,range(1,x+1)
)
print(factorial(3))
# 6
print(factorial(4))
# 24
print(factorial(5))
# 120
来以一个例子看看为什么龟叔会把移走(参考文献[2]):
total=reduce(
lambdaa,b:(,a[1]+b[1]),
items
)[1]
你能一眼搞清上述代码是做什么的吗?好像不太行,可能需要两到三眼。上述代码等效写法是:
defcombine(a,b):
return(,a[1]+b[1])
total=reduce(combine,items)[1]
好些了,还是不太清楚,再来看看不用的写法:
total=
fora,binitems:
total+=b
所以,有时候会帮助你写出让别人眼瞎的代码。
filter过滤器
除了和之外,也是一个十分常用的可迭代对象操作函数。它也是一个映射过程,只不过操作变成了条件,即,将一个可迭代对象中符合条件的元素筛选出来,组成一个新的可迭代对象。如果的列表推导式写法是这样的:,那么等价于这个:。第一个参数同样需要一个函数,只不过按照这个函数返回值的真假来进行筛选:
# 筛选出奇数
L=list(range(10))
odd=filter(lambdax:x%2,L)
print(list(odd))
# [1, 3, 5, 7, 9]
想要留下返回值为假的元素,可以直接使用标准库中的过滤器:
# 筛选出偶数
fromitertoolsimportfilterfalse
even=filterfalse(
lambdax:x%2,L
)
print(list(even))
# [0, 2, 4, 6, 8]
注:永远要优先使用标准库,标准库所提供的一定是最好的解决方案。
max,min和sum
这三个函数使用起来很简单,最大值最小值和求和。这里提一下,和均存在一个可选参数。我们可以利用来自定义比较对象。例如,
a= [(x,y)forx,yin
zip(range(3),range(3,,-1))]
print(a)
# [(0, 3), (1, 2), (2, 1)]
对于元组,默认以第一项的大小来比较,所以,
print(max(a))
# (2, 1)
如果想以第二项来比较,可以传递一个,让它取第二个值来比较:
print(max(a,key=lambdax:x[1]))
# (0, 3)
当然我们可以自定义比较方式:
print(min(
a,
key=lambdax:x[]-x[1]
))
# (0, 3)
下面来看一下上篇内容的最后一个例子:
importrandom
l= [433,787,868,915]
f=random.random()*1000
# 生成一个1000以内的随机数
print(f)
# 790.9193597866413
defargmin(seq):
importoperator
returnmin(
enumerate(seq),
key=operator.itemgetter(1)
)[]
out=lambdaf,l:l[
argmin(map(
lambdax:abs(x-f),
l
))
]
print(out(f,l))
# 787
其中获取了索引+值组成的序列,而实际上同作用一样,取出元素的第二个维度的值(也即的值),返回了上述值中的最小值所对应的元素(索引,值),最后的取到了最小值的第一个维度的值(也即的索引)。最终实现了的功能。注:在这种确定的情况下,请优先使用而不是表达式,因为它更加清晰紧致。
all和any
接触过MATLAB的一定对他俩不陌生。和用于判断一个序列是否满足特定的条件。指是否全部符合某个条件,而判断是否至少有一个满足条件。
print(all([1,1,1]))
# True
print(all([1,,1]))
# False
print(any([,,]))
# False
print(any([,1,]))
# True
sorted
用于对可迭代对象进行排序。当然,你可以自定义比较函数,比较对象以及是否反序,返回值依旧是迭代器:
fromrandomimportshuffle
l=list(range(5))
shuffle(l)
print(l)
# [4, 0, 2, 3, 1]
print(list(sorted(l,reverse=True)))
# [0, 1, 2, 3, 4]
info= [
('John',10,11,'m'),
('Mary',20,10,'f'),
('Jane',15,15,'f'),
('Lora',20,30,'f'),
('Ben',10,20,'m')
]
# 先按第二列升序再按第三列降序排列
frompprintimportpprint
pprint(list(
sorted(
info,
key=lambdax: (x[1],-x[2])
)
))
# [('Ben', 10, 20, 'm'),
# ('John', 10, 11, 'm'),
# ('Jane', 15, 15, 'f'),
# ('Lora', 20, 30, 'f'),
# ('Mary', 20, 10, 'f')]
参考文献
[1] MapReduce: Simplified Data Processing on Large Clusters:https://ai.google/research/pubs/pub62[2]https://docs.python.org/dev/howto/functional.html
友情链接: https://vonalex.github.io/
欢迎关注 它不只是Python
领取专属 10元无门槛券
私享最新 技术干货