深入理解Python reduce函數(shù)

官方解釋:

將一個可迭代的對象應(yīng)用到一個帶有兩個參數(shù)的方法上,我們稱之為appFun,遍歷這個可迭代對象,將其中的元素依次作為appFun的參數(shù),但這個函數(shù)有兩個參數(shù),作為哪個參數(shù)呢?有這樣的規(guī)則,看一下下面reduce方法的實現(xiàn),有三個參數(shù),第一個參數(shù)就是上面說的appFun,第二個參數(shù)就是那個可迭代的對象,而第三個呢?當(dāng)調(diào)用reduce方法的時候給出了initializer這個參數(shù),那么第一次調(diào)用appFun的時候這個參數(shù)值就作為第一個參數(shù),而可迭代對象的元素依次作為appFun的第二個參數(shù);如果調(diào)用reduce的時候沒有給出initializer這個參數(shù),那么第一次調(diào)用appFun的時候,可迭代對象的第一個元素就作為appFun的第一個元素,而可迭代器的從第二個元素到最后依次作為appFun的第二個參數(shù),除第一次調(diào)用之外,appFun的第一個參數(shù)就是appFun的返回值了。例如reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]),計算1到5的和,因為沒有給定initializer參數(shù),所以第一次調(diào)用x+y時,x=1,即列表的第一個元素,y=2,即列表的第二個元素,之后返回的1+2的結(jié)果作為第二次調(diào)用x+y中的x,即上一次的結(jié)果,y=2,即第二個元素,依次類推,知道得到1+2+3+4+5的結(jié)果。

這樣看來,其實下面的代碼定義是有一點(diǎn)問題,我們在程序中調(diào)用這段代碼reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]),得到的結(jié)果為16,而正確的結(jié)果為15,問題在于如果集合不是以0開始,那么按照如下代碼,第一次調(diào)用x=1,即第一個元素,y也是等于1,也是第一個元素,而正確的y應(yīng)該是2。所以真正的reduce方法應(yīng)該和下面的例子是有差別的。

 def reduce(function, iterable, initializer=None):
    it = iter(iterable)
    if initializer is None:
        try:
            initializer = next(it)
        except StopIteration:
            raise TypeError('reduce() of empty sequence with no initial value')
    accum_value = initializer
    for x in iterable:
        accum_value = function(accum_value, x)
    return accum_value

reduce函數(shù)能做什么,什么情況下要用reduce呢,看下面的例子:

例如上面的例子,實現(xiàn)一個整形集合的累加。假設(shè)lst = [1,2,3,4,5],實現(xiàn)累加的方式有很多:

第一種:用sum函數(shù)。

sum(lst)
第二種:循環(huán)方式。

def customer_sum(lst):
result = 0
for x in lst:
result+=x
return result

或者

def customer_sum(lst):
result = 0
while lst:
temp = lst.pop(0)
result+=temp
return result

if name=="main":
lst = [1,2,3,4,5]
print customer_sum(lst)
第三種:遞推求和

def add(lst,result):
if lst:
temp = lst.pop(0)
temp+=result
return add(lst,temp)
else:
return result

if name=="main":
lst = [1,2,3,4,5]
print add(lst,0)
第四種:reduce方式

lst = [1,2,3,4,5]
print reduce(lambda x,y:x+y,lst)

這種方式用lambda表示當(dāng)做參數(shù),因為沒有提供reduce的第三個參數(shù),所以第一次執(zhí)行時x=1,y=2,第二次x=1+2,y=3,即列表的第三個元素

或者

lst = [1,2,3,4,5]
print reduce(lambda x,y:x+y,lst,0)

這種方式用lambda表示當(dāng)做參數(shù),因為指定了reduce的第三個參數(shù)為0,所以第一次執(zhí)行時x=0,y=1,第二次x=0+1,y=2,即列表的第二個元素,假定指定reduce的第三個參數(shù)為100,那么第一次執(zhí)行x=100,y仍然是遍歷列表的元素,最后得到的結(jié)果為115

或者

def add(x,y):
return x+y

print reduce(add, lst)

與方式1相同,只不過把lambda表達(dá)式換成了自定義函數(shù)

或者

def add(x,y):
return x+y

print reduce(add, lst,0)

與方式2相同,只不過把lambda表達(dá)式換成了自定義函數(shù)

再舉一個例子:

有一個序列集合,例如[1,1,2,3,2,3,3,5,6,7,7,6,5,5,5],統(tǒng)計這個集合所有鍵的重復(fù)個數(shù),例如1出現(xiàn)了兩次,2出現(xiàn)了兩次等。大致的思路就是用字典存儲,元素就是字典的key,出現(xiàn)的次數(shù)就是字典的value。方法依然很多

第一種:for循環(huán)判斷

def statistics(lst):
dic = {}
for k in lst:
if not k in dic:
dic[k] = 1
else:
dic[k] +=1
return dic

lst = [1,1,2,3,2,3,3,5,6,7,7,6,5,5,5]
print(statistics(lst))
第二種:比較取巧的,先把列表用set方式去重,然后用列表的count方法

def statistics2(lst):
m = set(lst)
dic = {}
for x in m:
dic[x] = lst.count(x)

return dic

lst = [1,1,2,3,2,3,3,5,6,7,7,6,5,5,5]
print statistics2(lst)
第三種:用reduce方式

def statistics(dic,k):
if not k in dic:
dic[k] = 1
else:
dic[k] +=1
return dic

lst = [1,1,2,3,2,3,3,5,6,7,7,6,5,5,5]
print reduce(statistics,lst,{})

提供第三個參數(shù),第一次,初始字典為空,作為statistics的第一個參數(shù),然后遍歷lst,作為第二個參數(shù),然后將返回的字典集合作為下一次的第一個參數(shù)

或者
d = {}
d.extend(lst)
print reduce(statistics,d)

不提供第三個參數(shù),但是要在保證集合的第一個元素是一個字典對象,作為statistics的第一個參數(shù),遍歷集合依次作為第二個參數(shù)

通過上面的例子發(fā)現(xiàn),凡是要對一個集合進(jìn)行操作的,并且要有一個統(tǒng)計結(jié)果的,能夠用循環(huán)或者遞歸方式解決的問題,一般情況下都可以用reduce方式實現(xiàn)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容