2010-01-20 62 views
108

是否有更簡潔,高效或簡單的pythonic方式來執行以下操作?返回列表的產品

def product(list): 
    p = 1 
    for i in list: 
     p *= i 
    return p 

編輯:

其實我覺得這是略高於使用operator.mul快:

from operator import mul 
# from functools import reduce # python3 compatibility 

def with_lambda(list): 
    reduce(lambda x, y: x * y, list) 

def without_lambda(list): 
    reduce(mul, list) 

def forloop(list): 
    r = 1 
    for x in list: 
     r *= x 
    return r 

import timeit 

a = range(50) 
b = range(1,50)#no zero 
t = timeit.Timer("with_lambda(a)", "from __main__ import with_lambda,a") 
print("with lambda:", t.timeit()) 
t = timeit.Timer("without_lambda(a)", "from __main__ import without_lambda,a") 
print("without lambda:", t.timeit()) 
t = timeit.Timer("forloop(a)", "from __main__ import forloop,a") 
print("for loop:", t.timeit()) 

t = timeit.Timer("with_lambda(b)", "from __main__ import with_lambda,b") 
print("with lambda (no 0):", t.timeit()) 
t = timeit.Timer("without_lambda(b)", "from __main__ import without_lambda,b") 
print("without lambda (no 0):", t.timeit()) 
t = timeit.Timer("forloop(b)", "from __main__ import forloop,b") 
print("for loop (no 0):", t.timeit()) 

給我

('with lambda:', 17.755449056625366) 
('without lambda:', 8.2084708213806152) 
('for loop:', 7.4836349487304688) 
('with lambda (no 0):', 22.570688009262085) 
('without lambda (no 0):', 12.472226858139038) 
('for loop (no 0):', 11.04065990447998) 
+0

的零結果不是很有趣。有趣的是你在什麼平臺上使用的是什麼版本的Python。 – 2010-01-21 22:46:34

+0

不 - 我只是添加了零,因爲我意識到wiso的答案包括零,我想知道它有多大的差異。 我在ubuntu 9.10上使用python 2.6.4。 – 2010-01-21 23:05:43

+3

這裏給出的選項之間有一個功能差異,對於一個空列表,'reduce'應答引發一個'TypeError',而'for'循環應答返回1.這是'for'循環應答中的一個錯誤(一個空列表的產品不會超過17或'犰狳')。 – 2010-01-24 08:28:02

回答

127

不使用拉姆達:

from operator import mul 
reduce(mul, list, 1) 

它更好更快。與Python 2.7.5

from operator import mul 
import numpy as np 
import numexpr as ne 
# from functools import reduce # python3 compatibility 

a = range(1, 101) 
%timeit reduce(lambda x, y: x * y, a) # (1) 
%timeit reduce(mul, a)     # (2) 
%timeit np.prod(a)      # (3) 
%timeit ne.evaluate("prod(a)")   # (4) 

在以下配置:

a = range(1, 101) # A 
a = np.array(a) # B 
a = np.arange(1, 1e4, dtype=int) #C 
a = np.arange(1, 1e5, dtype=float) #D 

結果與Python 2.7.5

 

     |  1  |  2  |  3  |  4  | 
-------+-----------+-----------+-----------+-----------+ 
A  20.8 µs  13.3 µs  22.6 µs  39.6 µs  
B  106 µs  95.3 µs  5.92 µs  26.1 µs 
C  4.34 ms  3.51 ms  16.7 µs  38.9 µs 
D  46.6 ms  38.5 ms  180 µs  216 µs 

結果:np.prod是最快的國家之一,如果你使用np.array爲數據結構(小陣列爲18x,大陣列爲250x)

機智h python 3.3.2:

 

     |  1  |  2  |  3  |  4  | 
-------+-----------+-----------+-----------+-----------+ 
A  23.6 µs  12.3 µs  68.6 µs  84.9 µs  
B  133 µs  107 µs  7.42 µs  27.5 µs 
C  4.79 ms  3.74 ms  18.6 µs  40.9 µs 
D  48.4 ms  36.8 ms  187 µs  214 µs 

python 3是否比較慢?

+1

非常有趣,謝謝。任何想法爲什麼python 3可能會更慢? – 2010-01-20 22:16:58

+2

可能的原因:(1)Python 3'int'是Python 2'long'。 Python 2將使用「int」直到它溢出32位; Python 3將從一開始就使用「long」。 (2)Python 3.0是「概念驗證」。儘快升級到3.1! – 2010-01-20 22:30:59

+1

我重做相同的測試的其他機器上: 蟒2.6 ( '與拉姆達:',21.843887090682983) ( '無拉姆達:',9.7096879482269287) 蟒3.1: 與拉姆達:24.7712180614 無拉姆達: 10.7758350372 – 2010-01-21 11:25:22

39
reduce(lambda x, y: x * y, list, 1) 
+2

+1,但請參閱@ wiso關於'operator.mul'的回答,以獲得更好的方式。 – 2010-01-20 21:37:09

+1

失敗,爲空列表。 – bug 2013-01-26 18:43:56

+0

謝謝。增加第三個參數。 – 2013-01-30 12:10:00

16
import operator 
reduce(operator.mul, list, 1) 
+1

是最後一個參數(1)真的有必要嗎? – 2010-01-20 21:47:52

+5

如果列表可能爲空,則最後一個參數是必需的,否則將引發TypeError異常。當然,有時例外將是你想要的。 – 2010-01-21 00:06:53

+2

對於我來說,它返回0沒有這個參數,所以你也可以認爲有必要執行空產品約定。 – bug 2013-01-26 18:42:44

9

我記得在comp.lang.python上一些長期討論(抱歉,懶得現在產生指針)得出的結論是你原來product()定義是最Python化

請注意,建議不要在每次要寫入for循環時編寫for循環,而要編寫一次函數(每種減少類型)並根據需要調用它!減少調用函數是非常Python的 - 它與發電機表情甜蜜地工作,而且由於SUCESSFUL引進sum(),Python會越來越大,越來越內建功能降低 - any()all()是最新加入...

這一結論是有點兒官方 - reduce()來自內建removed在Python 3.0,他說:

「使用functools.reduce()如果你真的需要它;然而,時間明確的for循環99%更具有可讀性。」

另請參閱The fate of reduce() in Python 3000以獲得Guido的支持報價(以及一些Lispers支持閱讀該博客的支持評論)。

P.S.如果碰巧你需要組合product(),請參閱math.factorial()(新2.6)。

+1

+1準確(據我所知)的流行情緒Python社區 - 儘管我肯定更喜歡在這種情況下反對所說的流行情緒,但最好還是知道它們是什麼。另外,我喜歡關於來自LtU的不支持的Lispers(我猜可能是其中之一)。 :-) – 2010-01-23 20:42:17

5

此答案的目的是提供一種計算即在某些情況下有用的 - 即當一個)有乘以大量的值,使得最終產品可以是非常大或非常小,和b ),你真的不關心確切的答案,而是有一個數列,並希望能夠根據每個人的產品進行排序。

如果要乘一個列表,其中L爲列表中的元素,你可以這樣做:

import math 
math.exp(sum(map(math.log, l))) 

現在,這種方法並不像

from operator import mul 
reduce(mul, list) 

的可讀性如果您對於一個不熟悉reduce()的數學家來說可能是正確的,但我不建議在正常情況下使用它。它也比問題中提到的product()函數更不可讀(至少對非數學家來說)。

但是,如果你在一個情況是永遠在那裏,你的風險溢或上溢,如

>>> reduce(mul, [10.]*309) 
inf 

,你的目的是比較不同序列的產品,而不是知道產品是什麼,然後

>>> sum(map(math.log, [10.]*309)) 
711.49879373515785 

是去,因爲它幾乎不可能有實際問題中,你會溢出或下溢本辦法的辦法。 (該計算的結果是越大,產品的是,如果你可以計算。)

+0

這很聰明,但如果您有任何負值或零值,則會失敗。 :/ – 2017-03-09 12:19:07

29

,如果你只是在你的列表編號:

from numpy import prod 
prod(list) 

編輯:如通過@指出off99555這對於大整數結果在這種情況下,它返回而基於operator.mulreduce作品大整數結果伊恩Clelland的解決方案,因爲它返回longnumpy.int64類型的結果是行不通的。

+0

這是比較慢,如果列表很短 – endolith 2016-04-25 16:03:24

+1

我試圖評估'從numpy進口prod; prod(list(range(5,101)))'並且它輸出了'0',你能在Python 3上重現這個結果嗎? – off99555 2016-11-27 11:57:49

+0

,因爲'prod'在這種情況下返回'numpy.int64'類型的結果,並且您已經爲'range(5,23)'得到了一個溢出(實際爲負值)。使用@Ian Clelland的基於'operator.mul'和'reduce'的解決方案來實現大整數(在這種情況下它返回一個'long',這似乎具有任意的精度)。 – 2016-11-27 13:31:42

-3

這也適用,雖然它的欺騙

def factorial(n): 
    x=[] 
    if n <= 1: 
     return 1 
    else: 
     for i in range(1,n+1): 
      p*=i 
      x.append(p) 
     print x[n-1]  
+0

我已經修復了縮進,但我認爲你應該用返回來替換最後的'print'。另外,不需要將中間值存儲在列表中,只需在「迭代」之間存儲「p」即可。 – BoppreH 2013-10-18 18:54:31

+0

factorial?列表的產品不是因子...。 – 2016-08-31 18:55:51

8

那麼,如果你真的想使一行沒有進口任何物件,你可以這樣做:

eval('*'.join(str(item) for item in list)) 

但是不要。

+1

非常非常聰明! – lifebalance 2017-06-15 15:24:06

1

我很驚訝,沒有人使用了與itertools.accumulate建議operator.mul。這避免了使用reduce,這對於Python 2和3個不同的(由於用於Python 3所需的functools進口),並且此外被認爲是未Python化by Guido van Rossum himself

from itertools import accumulate 
from operator import mul 

def prod(lst): 
    for value in accumulate(lst, mul): 
     pass 
    return value 

實施例:

prod([1,5,4,3,5,6]) 
# 1800