返回列表的产品

时间:2010-01-20 20:40:11

标签: python

是否有更简洁,高效或简单的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)

16 个答案:

答案 0 :(得分:157)

不使用lambda:

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)

使用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 :(得分:44)

reduce(lambda x, y: x * y, list, 1)

答案 2 :(得分:38)

如果您的列表中只有数字:

from numpy import prod
prod(list)

编辑:正如@ off99555所指出的,这对大整数结果不起作用,在这种情况下,它返回类型为numpy.int64的结果,而Ian Clelland的解决方案基于{{1} } operator.mul适用于大整数结果,因为它返回reduce

答案 3 :(得分:17)

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

答案 4 :(得分:15)

好吧,如果你真的想让它成为一行而不进行任何你可以做的事情:

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

但不要。

答案 5 :(得分:14)

我记得有一些关于comp.lang.python的讨论(对不起,现在懒得生成指针),结论是你的原始product()定义是最Pythonic

请注意,建议不是每次都要编写for循环,而是编写一次函数(每种类型的减少)并根据需要调用它!调用约简函数非常Pythonic - 它与生成器表达式很好地协同工作,自从成功引入sum()以来,Python不断发展越来越多的内置约简函数 - any()all()是最新的加法...

这个结论有点官方 - reduce()来自Python 3.0内置的removed,说:

  

“如果确实需要,请使用functools.reduce();但是,99%的时间显式for循环更具可读性。”

另请参阅The fate of reduce() in Python 3000以获取Guido的支持报价(以及阅读该博客的Lispers的一些支持性评论)。

P.S。如果您需要product()组合词,请参阅math.factorial()(新2.6)。

答案 6 :(得分:7)

这个答案的目的是提供一个在某些情况下有用的计算 - 即当a)有大量的值乘以使最终产品可能非常大或极小时,和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

是要走的路,因为实际上不可能出现这种方法会溢出或下溢的现实问题。 (计算结果越大,如果 计算它,产品就越大。)

答案 7 :(得分:7)

Python 3.8开始,prod函数已包含在标准库的math模块中:

  

math.prod(iterable,*,start = 1)

返回一个start值(默认值:1)乘以一个可迭代数字的乘积:

import math

math.prod([2, 3, 4]) # 24

请注意,如果iterable为空,则会产生1(或start值(如果提供))。

答案 8 :(得分:2)

我很惊讶没有人建议将itertools.accumulateoperator.mul一起使用。这避免了使用reduce,这对于Python 2和3来说是不同的(由于Python 3需要functools导入),而且被认为是非pythonic 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

答案 9 :(得分:1)

一种选择是使用numba@jit or @njit decorator。我还对您的代码进行了一两次小的调整(至少在Python 3中,“列表”是不应用作变量名的关键字):

@njit
def njit_product(lst):
    p = lst[0]  # first element
    for i in lst[1:]:  # loop over remaining elements
        p *= i
    return p

出于计时目的,您需要先运行一次以使用numba编译函数。通常,该函数将在首次调用时进行编译,然后在内存中调用(更快)。

njit_product([1, 2])  # execute once to compile

现在,当您执行代码时,它将与函数的编译版本一起运行。我使用Jupyter笔记本和%timeit魔术功能为它们计时:

product(b)  # yours
# 32.7 µs ± 510 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

njit_product(b)
# 92.9 µs ± 392 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

请注意,在运行Python 3.5的计算机上,原生Python for循环实际上是最快的。在使用Jupyter笔记本电脑和%timeit魔术功能来测量数字装饰性能时,这里可能会有一个技巧。我不确定上述时间是否正确,因此建议您在系统上试用一下,看看numba是否可以提高性能。

答案 10 :(得分:0)

我发现最快的方法是使用while:

mysetup = '''
import numpy as np
from find_intervals import return_intersections 
'''

# code snippet whose execution time is to be measured
mycode = '''

x = [4,5,6,7,8,9,10]
prod = 1
i = 0
while True:
    prod = prod * x[i]
    i = i + 1
    if i == len(x):
        break
'''

# timeit statement for while:
print("using while : ",
timeit.timeit(setup=mysetup,
              stmt=mycode))

# timeit statement for mul:
print("using mul : ",
    timeit.timeit('from functools import reduce;
    from operator import mul;
    c = reduce(mul, [4,5,6,7,8,9,10])'))

# timeit statement for mul:
print("using lambda : ",      
    timeit.timeit('from functools import reduce;
    from operator import mul;
    c = reduce(lambda x, y: x * y, [4,5,6,7,8,9,10])'))

时间是:

>>> using while : 0.8887967770060641

>>> using mul : 2.0838719510065857

>>> using lambda : 2.4227715369997895

答案 11 :(得分:0)

我已经用perfplot(我的一个小项目)测试了各种解决方案,发现了

numpy.prod(lst)
到目前为止,

是最快的解决方案(如果列表不是很短)。

enter image description here


用于复制情节的代码:

import perfplot
import numpy

from operator import mul
from functools import reduce

from itertools import accumulate


def reduce_lambda(lst):
    return reduce(lambda x, y: x * y, lst)


def reduce_mul(lst):
    return reduce(mul, lst)


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


def numpy_prod(lst):
    return numpy.prod(lst)


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


perfplot.show(
    setup=numpy.random.rand,
    kernels=[
       reduce_lambda,
       reduce_mul,
       forloop,
       numpy_prod,
       itertools_accumulate,
    ],
    n_range=[2**k for k in range(15)],
    xlabel='len(a)',
    logx=True,
    logy=True,
    )

答案 12 :(得分:0)

OP的Python 3测试结果:(每次3个最好)

with lambda: 18.978000981995137
without lambda: 8.110567473006085
for loop: 10.795806062000338
with lambda (no 0): 26.612515013999655
without lambda (no 0): 14.704098362999503
for loop (no 0): 14.93075215499266

答案 13 :(得分:0)

我不确定最快的方法,但这里是获取任何集合的产品而不导入任何库或模块的短代码。

eval('*'.join(map(str,l)))

答案 14 :(得分:-2)

代码如下:

product = 1 # Set product to 1 because when you multiply it you don't want you answer to always be 0
my_list = list(input("Type in a list: ").split(", ")) # When input, the data is a string, so you need to convert it into a list and split it to make it a list.
for i in range(0, len(my_list)):
  product *= int(my_list[i])
print("The product of all elements in your list is: ", product)

答案 15 :(得分:-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]