2010-01-20 154 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