本文摘自《流暢的Python》(《FluentPython》)游岳,作者Luciano Ramalho只损,譯者安道 吳珂
=============
序言
就像“什么是美”沒有確切的答案一樣掠械,“什么是Python風(fēng)格”也沒有標(biāo)準(zhǔn)答案微王。如果回答“地道的Python”累提,不能讓人100%滿意判呕,因為對你來說是“地道的”倦踢,在我看來卻可能不是。但我可以肯定的是侠草,“地道”并不是指使用最鮮為人知的語言特性辱挥。
先決條件
- 歸約函數(shù)(reduce、sum边涕、any晤碘、all)將序列或有限的迭代對象變成一個聚合結(jié)果
- reduce函數(shù)在目前的更新中已經(jīng)被收進(jìn)functools包中了
- 函數(shù)簽名
reduce(function, iterable, initializer)
,
其中第三個參數(shù)initializer
是可選的功蜓。它避免了空序列拋出異常园爷,如果令了這個參數(shù),將從這個參數(shù)開始式撼。(一般為恒等值童社,如+|^等令為1,+ &令為0)端衰。function
是一個接收兩個參數(shù)的函數(shù)叠洗。 - 調(diào)用的一般過程為:如序列[1,2,3,4,5]
fun(1,2) (=a)
fun(a, 3) (=b)
fun(b, 4) etc...
問題提出
Python-list上有一篇題為“Pythonic Way to Sum n-th List Element?”(鏈接似已失效?)的話題與本篇討論的reduce函數(shù)有關(guān)旅东。
該話題發(fā)起人Guy Middleton說他不喜歡使用lambda表達(dá)式,問如下方案可否改進(jìn):
>>>my_list = [[1, 2, 3], [40, 50, 60], [9, 8, 7]]
>>>import functools
>>>functools.reduce(lambda a,, b: a+b, [sub[1] for sub in my_list])
60
這段代碼包含了:lambda十艾、reduce和列表推導(dǎo)抵代。這對于討厭lambda和看不上列表推導(dǎo)的人兩邊不討好——這兩種人都很多。如果使用lambda忘嫉,或許就不應(yīng)該使用列表推導(dǎo)——過濾除外荤牍,但這不是過濾((for x in list if x > 0)
其中if
就是過濾表達(dá)式)案腺。
=============
本書的作者給出的方案是:
>>> functools.reduce(lambda a, b: a + b[1], my_list, 0)
60
但作者表示不會在真實代碼如此寫,(因為他也不喜歡lambda表達(dá)式)康吵。此處僅僅是為了舉例說明不使用列表推導(dǎo)怎么做劈榨。
=============
第一個答案
,來自Fernando Perez晦嵌,IPython的創(chuàng)建者同辣,強(qiáng)調(diào)了NumPy支持n維數(shù)組和n維切片:
>>> import numpy as np
>>>my_array = np.array(my_list)
>>>np.sum(my_array[:, 1]
60
(即一維全體,二維的下標(biāo)1元素)
=============
第二個答案
惭载,Guy Middleton推崇Paul Rubin和Skip Montanaro給出的下述方案:
>>> import operator
>>> functools.reduce(operator.add, my_list, 0)
60
其中旱函,operator
庫中包含諸如add
, xor
等常用數(shù)字運算函數(shù),包含兩個參數(shù)
=============
以及
描滔,EvanSimpson問道:"這樣做有什么錯棒妨?"
>>> total = 0
>>> for sub in my_list:
... total += sub[1]
>>>total
60
foreach循環(huán)。
許多人都覺得這也很符合Python風(fēng)格含长。Alex Martelli甚至說券腔,Guido或許就會這么做。
作者喜歡這段代碼拘泞,更喜歡David Eppstein對此給出的評論:
如果你想計算列表各個元素的和颅眶,寫出的代碼應(yīng)該看起來像是在“計算元素之和”,而不是“迭代元素田弥,維護(hù)一個變量t涛酗,再執(zhí)行一系列求和操作”。如果不能站在一定高度上表明意圖偷厦,讓語言去關(guān)注低層次操作商叹,那么要高級語言干嘛?
之后Alex Martelli又建議:
求和操作經(jīng)常需要只泼,我不介意Python提供一個這樣的內(nèi)置函數(shù)剖笙。但是,在我看來请唱,“reduce(operator..add,...”不是好方法(作為一名APL老程序員和FP語言的愛好者弥咪,我應(yīng)該喜歡,但我并不喜歡)十绑。
隨后
Alex建議提供并實現(xiàn)了sum()
函數(shù)并在之后的Python2.3中內(nèi)置了聚至。因此,Alex喜歡的句法變成了標(biāo)準(zhǔn):(列表推導(dǎo)本橙,僅能生成list)
>>> sum([sub[1] for sub in my_list])
60
下一年年末(2004年11月)扳躬,Python2.4發(fā)布,這一版引入了生成器表達(dá)式。因此贷币,作者建議击胜,當(dāng)前這個問題最符合Python風(fēng)格的答案是:(生成任何類型的序列)
>>> sum(sub[1] for sub in my_list)
60
這樣寫不僅比reduce函數(shù)可讀性更強(qiáng),而且還避免了空序列導(dǎo)致的陷阱:sum([])
的結(jié)果是0役纹,就這么簡單
在這次討論中偶摔,Alex Martelli指出,Python2內(nèi)置的reduce函數(shù)成事不足敗事有余促脉,因為他推薦的地道編程方式難以理解辰斋。他的觀點最優(yōu)說服力:Python3把reduce函數(shù)移到functools模塊中了。