如何使用Python将列表中的所有项相乘?

274

如何编写代码将给定的数字列表相乘,例如 [1,2,3,4,5,6],计算结果应为1*2*3*4*5*6

15个回答

256

Python 3:使用functools.reduce函数:

>>> from functools import reduce
>>> reduce(lambda x, y: x*y, [1, 2, 3, 4, 5, 6])
720

Python 2:使用reduce函数:

>>> reduce(lambda x, y: x*y, [1, 2, 3, 4, 5, 6])
720

要兼容2和3,使用Six库(pip install six),然后:

>>> from six.moves import reduce
>>> reduce(lambda x, y: x*y, [1,2,3,4,5,6])
720

33
我测试了从1到100的数字相乘。在Python2和3中,使用lambda函数平均每1000次操作需要0.02秒,而使用operator.mul函数平均每1000次操作需要0.009秒,因此operator.mul的速度比lambda函数快一个数量级。 - whereswalden
4
可能是因为经过额外的函数(lambda)处理会增加开销,而operator.mul可以直接进入C。 - whereswalden
4
我不会将0.009和0.02相比,认为它们相差一个数量级。其实只是差不到一半。 - jlh
8
从Python 3.8开始,可以使用math.prod([1,2,3,4,5,6])来简单完成。 (当然需要导入math模块) - Tomerikoo
1
您无需安装 six,即可在 Python 2.6 或 2.7 中使用与 Python 3 相同的语法。您只需要使用与 Python 3 中相同的行 from functools import reduce 即可。 - Alan
显示剩余2条评论

194

您可以使用:

import operator
import functools
functools.reduce(operator.mul, [1,2,3,4,5,6], 1)

有关说明,请参阅reduceoperator.mul文档。

在Python 3+中,您需要加入import functools模块。


32
请注意,在Python3中,reduce()函数已从全局命名空间中移除,并放置在functools模块中。因此,在Python3中,您需要使用语句 from functools import reduce - Eugene Yarmash
2
这里第三个参数的'1'是不必要的,什么情况下会需要它呢? - wordsforthewise
7
如果你传递一个空序列给@wordsforthewise而没有第三个参数,它会抛出TypeError异常。 - Francisco

120

我将使用numpy.prod来执行任务:

import numpy as np

mylist = [1, 2, 3, 4, 5, 6] 
result = np.prod(np.array(mylist))  

20
如果您已经在使用Numpy,这将非常方便。您可能甚至不需要首先将其转换为列表,对于大多数情况,这应该可以工作:result = np.prod(mylist) - Nick
6
需要注意两点: 1) 如果使用默认的numpy.int32,可能会发生溢出 2) 对于较小的列表,这将明显变慢,因为NumPy需要分配一个数组(如果经常重复使用,则相关) - Disenchanted
2
在这里,对于大于21的值会发生溢出:np.prod(np.array(range(1,21))) - PatrickT
这不是一个好的选择。它可能会溢出并且速度较慢。请尝试使用 reduce - Peyman

79

如果您想避免导入任何内容并避免使用更复杂的Python功能,您可以使用简单的for循环:

nums = [1, 2, 3]

product = 1  # Don't use 0 here, otherwise, you'll get zero 
             # because anything times zero will be zero.
for num in nums:
    product *= num

10
小注释:在Python中,片段很容易实现。由于我们只涉及原始数据类型,因此您可以通过从list[0]开始并迭代list[1:]来避免使用小技巧从1开始。尽管在这里熟悉更多的函数式“reduce”答案对于长期来说是有价值的,因为它在其他情况下也很有用。 - kungphu
5
空积通常定义为1,如果你传递一个空序列给它,你的解决方案会抛出IndexError异常。 - Francisco
@Francisco 确实如此,但是在这种情况下,该函数可能应该抛出某种异常,因为空序列对于此函数来说是无效输入。实际上,对于任何少于两个值的序列,此函数都没有意义;如果您传递一个具有一个值并将其乘以1的序列,则基本上添加了一个不存在的值,我认为这相当于意外行为。 - kungphu
3
@kungphu,这个答案的行为是正确的,即传递一个长度为1的列表返回该值,传递一个长度为0的列表返回1。这与将sum([])视为0或sum([3])视为3的思路相同。参见:https://en.wikipedia.org/wiki/Empty_product - emorris
1
我理解你关于数学函数的观点。然而,在实际开发中,一个明确旨在操作输入的函数在没有输入或无效输入的情况下返回值是非常罕见的。我想这取决于练习的目标:如果只是为了复制标准库,那么可以,也许它能教会人们如何实现语言。否则,我认为它错过了提供有关有效和无效参数的课程的好机会。 - kungphu

68
在Python 3.8及以上版本中,标准库模块math提供.prod用于此目的:
math.prod(iterable, *, start=1)
该方法返回一个起始值(默认为1)与数字可迭代对象相乘的积:
import math
math.prod([1, 2, 3, 4, 5, 6])
# 720

如果可迭代对象为空,这将生成 {{1}}(或提供的{{start}}值)。

16

以下是我机器的一些性能测量结果。如果在长时间运行的循环中处理小输入,则这些结果可能会有帮助:

import functools, operator, timeit
import numpy as np

def multiply_numpy(iterable):
    return np.prod(np.array(iterable))

def multiply_functools(iterable):
    return functools.reduce(operator.mul, iterable)

def multiply_manual(iterable):
    prod = 1
    for x in iterable:
        prod *= x

    return prod

sizesToTest = [5, 10, 100, 1000, 10000, 100000]

for size in sizesToTest:
    data = [1] * size

    timerNumpy = timeit.Timer(lambda: multiply_numpy(data))
    timerFunctools = timeit.Timer(lambda: multiply_functools(data))
    timerManual = timeit.Timer(lambda: multiply_manual(data))

    repeats = int(5e6 / size)
    resultNumpy = timerNumpy.timeit(repeats)
    resultFunctools = timerFunctools.timeit(repeats)
    resultManual = timerManual.timeit(repeats)
    print(f'Input size: {size:>7d} Repeats: {repeats:>8d}    Numpy: {resultNumpy:.3f}, Functools: {resultFunctools:.3f}, Manual: {resultManual:.3f}')

结果:

Input size:       5 Repeats:  1000000    Numpy: 4.670, Functools: 0.586, Manual: 0.459
Input size:      10 Repeats:   500000    Numpy: 2.443, Functools: 0.401, Manual: 0.321
Input size:     100 Repeats:    50000    Numpy: 0.505, Functools: 0.220, Manual: 0.197
Input size:    1000 Repeats:     5000    Numpy: 0.303, Functools: 0.207, Manual: 0.185
Input size:   10000 Repeats:      500    Numpy: 0.265, Functools: 0.194, Manual: 0.187
Input size:  100000 Repeats:       50    Numpy: 0.266, Functools: 0.198, Manual: 0.185
你可以看到,对于较小的输入,Numpy要慢得多,因为它在执行乘法之前需要分配一个数组。此外,请注意Numpy中的溢出问题。

你可以出于好奇心添加 eval 方法 - Mr_and_Mrs_D
2
我怀疑 multiply_functoolsmultiply_numpy 受到了查找 npfunctoolsoperator 全局变量以及属性查找的影响。您是否介意切换到本地变量?在函数签名中添加 _reduce=functools.reduce, _mul=operator.mul,然后在函数体中返回 _reduce(_mul, iterable)` 等。 - Martijn Pieters
3
同时,numpy版本需要先将数字转换为numpy数组;通常情况下,您已经进行了该转换,将其包含在时间测量中并不公平。将列表转换为numpy数组后,当元素数达到100个或更多时,“np.prod()”选项开始变得最快。 - Martijn Pieters

8

Numpy提供了prod()函数,用于返回列表或数组(在这里是Numpy数组)的给定轴上的乘积:

import numpy
a = [1,2,3,4,5,6]
b = numpy.prod(a)

...或者您可以直接导入numpy.prod()

from numpy import prod
a = [1,2,3,4,5,6]
b = prod(a)

7

个人认为,以下代码可用于将所有通用列表元素相乘的函数:

def multiply(n):
    total = 1
    for i in range(0, len(n)):
        total *= n[i]
    print total

这段代码很简洁,只用了一个变量和一个for循环,对我来说感觉很直观(它看起来就像我想到这个问题的方式一样:取一个数,乘以它,然后再乘以下一个数,以此类推!)


8
为什么不用 for i in n:,然后 total *= i 呢?这样不是更简单吗? - Munim Munna

5
简单的方法是:
import numpy as np
np.exp(np.log(your_array).sum())

12
只需使用np.prod(your_Array)即可。 - dashesy
a) 取对数是很耗费资源的。 b) 对于零和负值表现不佳。可以通过使用绝对值、计算负数并在出现零时检查或抛出异常来解决。无论如何,这都是很耗费资源的。 - ShpielMeister

3
nums = str(tuple([1,2,3]))
mul_nums = nums.replace(',','*')
print(eval(mul_nums))

5
请在您的回答中添加一些解释。 - xenteros
3
我插话并尝试解释这段代码: 个人而言,我不太喜欢这段代码,因为它使用了 eval,它将字符串解释为参数或函数(因此通常被视为一种不安全的做法,特别是在处理输入数据时)。在那之前的那一行将每个分隔逗号替换为乘法符号 *,以便 eval 将其识别为乘法。我想知道这样的性能如何,特别是与其他解决方案相比较。 - dennlinger
3
哇,太糟糕的主意了! - Ekrem Dinçel
永远不要在可能来自程序外部的任何形式的数据上使用eval(或exec)。这是一个严重的安全风险。您允许数据的作者在您的计算机上运行任意代码。它不能轻松地被沙盒化,而适当的沙盒化比使用适当的工具更难。 - Karl Knechtel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接