python中的reduce内建函数使用方法指南
官方解释:
Applyfunctionoftwoargumentscumulativelytotheitemsofiterable,fromlefttoright,soastoreducetheiterabletoasinglevalue.Forexample,reduce(lambdax,y:x+y,[1,2,3,4,5])calculates((((1+2)+3)+4)+5).Theleftargument,x,istheaccumulatedvalueandtherightargument,y,istheupdatevaluefromtheiterable.Iftheoptionalinitializerispresent,itisplacedbeforetheitemsoftheiterableinthecalculation,andservesasadefaultwhentheiterableisempty.Ifinitializerisnotgivenanditerablecontainsonlyoneitem,thefirstitemisreturned.Roughlyequivalentto:
意思就是说:将一个可迭代的对象应用到一个带有两个参数的方法上,我们称之为appFun,遍历这个可迭代对象,将其中的元素依次作为appFun的参数,但这个函数有两个参数,作为哪个参数呢?有这样的规则,看一下下面reduce方法的实现,有三个参数,第一个参数就是上面说的appFun,第二个参数就是那个可迭代的对象,而第三个呢?当调用reduce方法的时候给出了initializer这个参数,那么第一次调用appFun的时候这个参数值就作为第一个参数,而可迭代对象的元素依次作为appFun的第二个参数;如果调用reduce的时候没有给出initializer这个参数,那么第一次调用appFun的时候,可迭代对象的第一个元素就作为appFun的第一个元素,而可迭代器的从第二个元素到最后依次作为appFun的第二个参数,除第一次调用之外,appFun的第一个参数就是appFun的返回值了。例如reduce(lambdax,y:x+y,[1,2,3,4,5]),计算1到5的和,因为没有给定initializer参数,所以第一次调用x+y时,x=1,即列表的第一个元素,y=2,即列表的第二个元素,之后返回的1+2的结果作为第二次调用x+y中的x,即上一次的结果,y=2,即第二个元素,依次类推,知道得到1+2+3+4+5的结果。
这样看来,其实下面的代码定义是有一点问题,我们在程序中调用这段代码reduce(lambdax,y:x+y,[1,2,3,4,5]),得到的结果为16,而正确的结果为15,问题在于如果集合不是以0开始,那么按照如下代码,第一次调用x=1,即第一个元素,y也是等于1,也是第一个元素,而正确的y应该是2。所以真正的reduce方法应该和下面的例子是有差别的。
defreduce(function,iterable,initializer=None): it=iter(iterable) ifinitializerisNone: try: initializer=next(it) exceptStopIteration: raiseTypeError('reduce()ofemptysequencewithnoinitialvalue') accum_value=initializer forxiniterable: accum_value=function(accum_value,x) returnaccum_value
那么reduce函数能做什么,什么情况下要用reduce呢,看下面的例子:
例如上面的例子,实现一个整形集合的累加。假设lst=[1,2,3,4,5],实现累加的方式有很多:
第一种:用sum函数。
sum(lst)
第二种:循环方式。
defcustomer_sum(lst): result=0 forxinlst: result+=x returnresult #或者 defcustomer_sum(lst): result=0 whilelst: temp=lst.pop(0) result+=temp returnresult if__name__=="__main__": lst=[1,2,3,4,5] printcustomer_sum(lst)
第三种:递推求和
defadd(lst,result): iflst: temp=lst.pop(0) temp+=result returnadd(lst,temp) else: returnresult if__name__=="__main__": lst=[1,2,3,4,5] printadd(lst,0)
第四种:reduce方式
lst=[1,2,3,4,5] printreduce(lambdax,y:x+y,lst) #这种方式用lambda表示当做参数,因为没有提供reduce的第三个参数,所以第一次执行时x=1,y=2,第二次x=1+2,y=3,即列表的第三个元素 #或者 lst=[1,2,3,4,5] printreduce(lambdax,y:x+y,lst,0) #这种方式用lambda表示当做参数,因为指定了reduce的第三个参数为0,所以第一次执行时x=0,y=1,第二次x=0+1,y=2,即列表的第二个元素, 假定指定reduce的第三个参数为100,那么第一次执行x=100,y仍然是遍历列表的元素,最后得到的结果为115 #或者 defadd(x,y): returnx+y printreduce(add,lst) #与方式1相同,只不过把lambda表达式换成了自定义函数 #或者 defadd(x,y): returnx+y printreduce(add,lst,0) #与方式2相同,只不过把lambda表达式换成了自定义函数
再举一个例子:有一个序列集合,例如[1,1,2,3,2,3,3,5,6,7,7,6,5,5,5],统计这个集合所有键的重复个数,例如1出现了两次,2出现了两次等。大致的思路就是用字典存储,元素就是字典的key,出现的次数就是字典的value。方法依然很多
第一种:for循环判断
defstatistics(lst): dic={} forkinlst: ifnotkindic: dic[k]=1 else: dic[k]+=1 returndic lst=[1,1,2,3,2,3,3,5,6,7,7,6,5,5,5] print(statistics(lst))
第二种:比较取巧的,先把列表用set方式去重,然后用列表的count方法
defstatistics2(lst): m=set(lst) dic={} forxinm: dic[x]=lst.count(x) returndic lst=[1,1,2,3,2,3,3,5,6,7,7,6,5,5,5] printstatistics2(lst)
第三种:用reduce方式
defstatistics(dic,k): ifnotkindic: dic[k]=1 else: dic[k]+=1 returndic lst=[1,1,2,3,2,3,3,5,6,7,7,6,5,5,5] printreduce(statistics,lst,{}) #提供第三个参数,第一次,初始字典为空,作为statistics的第一个参数,然后遍历lst,作为第二个参数,然后将返回的字典集合作为下一次的第一个参数 或者 d={} d.extend(lst) printreduce(statistics,d) #不提供第三个参数,但是要在保证集合的第一个元素是一个字典对象,作为statistics的第一个参数,遍历集合依次作为第二个参数
通过上面的例子发现,凡是要对一个集合进行操作的,并且要有一个统计结果的,能够用循环或者递归方式解决的问题,一般情况下都可以用reduce方式实现。
reduce函数真是“一位好同志啊”!