【问题标题】:Returning the product of a list返回列表的产品
【发布时间】:2017-02-04 07:45:18
【问题描述】:

是否有更简洁、高效或简单的 Python 方式来执行以下操作?

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

编辑:

我实际上发现这比使用 operator.mul 快一点:

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

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

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

def forloop(lst):
    r = 1
    for x in lst:
        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)

【问题讨论】:

  • 这里给出的选项之间存在功能差异,因为对于空列表,reduce 答案会引发 TypeError,而 for 循环答案会返回 1。这是for 循环回答(空列表的乘积不超过 1,不超过 17 或“犰狳”)。
  • 请尽量避免使用内置函数的名称(例如列表)作为变量名称。
  • 旧答案,但我很想编辑,所以它不使用 list 作为变量名...
  • 空列表的乘积是1。en.wikipedia.org/wiki/Empty_product
  • @ScottGriffiths 您是否还会声称空列表的总和不为 0?我强烈不同意您的说法,空列表的乘积是 1。

标签: python


【解决方案1】:
from functools import reduce

a = [1, 2, 3]
reduce(lambda x, y: x * y, a, 1)

【讨论】:

  • +1 但请参阅@wiso 对operator.mul 的回答以了解更好的方法。
  • 为什么 operator.mul 比 x*y 更受欢迎?
  • operator.mul 是一个函数,因此不仅可以替代 x*y,还可以替代整个 lambda 表达式(即reduce 的第一个参数)
  • 您必须导入 from functools import reduce 才能使其在 Python 3 中工作。
【解决方案2】:

不使用 lambda:

from operator import mul
# from functools import reduce # python3 compatibility
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 毫秒 3.51 毫秒 16.7 微秒 38.9 微秒 D 46.6 毫秒 38.5 毫秒 180 微秒 216 微秒

结果: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 毫秒 3.74 毫秒 18.6 微秒 40.9 微秒 D 48.4 毫秒 36.8 毫秒 187 微秒 214 微秒

python 3 慢吗?

【讨论】:

  • 非常有趣,谢谢。知道为什么 python 3 可能会更慢吗?
  • 可能的原因:(1)Python 3 int 是 Python 2 long。 Python 2 将使用“int”,直到它溢出 32 位; Python 3 从一开始就使用“long”。 (2) Python 3.0 是“概念证明”。尽快升级到 3.1!
  • 我在另一台机器上重做了同样的测试:python 2.6 ('with lambda:', 21.843887090682983) ('without lambda:', 9.7096879482269287) python 3.1: with lambda: 24.7712180614 without lambda: 10.7758350372
  • 两者都因空列表而失败。
  • 请注意,您必须从 Python 3 中的 functools 模块导入 reduce 运算符。 from functools import reduce.
【解决方案3】:
import operator
reduce(operator.mul, list, 1)

【讨论】:

  • 最后一个参数(1)真的有必要吗?
  • 如果列表可能为空,则最后一个参数是必需的,否则会抛出 TypeError 异常。当然,有时你想要的会是一个例外。
  • 对我来说它返回 0 没有那个参数,所以你也可以认为有必要强制执行空产品约定。
  • functools.reduce(..) in python3
【解决方案4】:

我记得关于 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 的一些不太支持的 cmets)。

附:如果您碰巧需要 product() 进行组合,请参阅 math.factorial()(新 2.6)。

【讨论】:

  • +1 用于准确(据我所知)对 Python 社区中流行情绪的描述——虽然在这种情况下我肯定更喜欢反对所说的流行情绪,但最好了解它们反正他们是什么。另外,我喜欢 LtU 中关于不支持 Lispers 的那一点(我想我会是其中之一)。 :-)
【解决方案5】:

此答案的目的是提供在某些情况下有用的计算 - 即当 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

是要走的路,因为在现实世界中,使用这种方法几乎不可能出现上溢或下溢的问题。 (该计算的结果越大,如果您可以计算它,产品就会越大。)

【讨论】:

  • 这很聪明,但如果你有任何负值或零值,它就会失败。 ://
【解决方案6】:

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

from numpy import prod
prod(list)

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

【讨论】:

  • 如果列表很短,这会更慢
  • 我尝试评估 from numpy import prod; prod(list(range(5,101))) 并输出 0,你能在 Python 3 上重现这个结果吗?
  • 因为在这种情况下prod 返回了numpy.int64 类型的结果,而range(5,23) 已经溢出(实际上是一个负值)。使用@Ian Clelland 的基于operator.mulreduce 的解决方案来处理大整数(在这种情况下,它返回一个long,它似乎具有任意精度)。
  • @off99555 两种解决方案:要么通过np.prod(np.arange(5.0,101.0)) 从浮点类型列表开始,要么通过np.prod(np.array(range(5,101)).astype(np.float64)) 将其转换为浮点类型。请注意,NumPy 使用 np.float64 而不是 float。我不知道有什么区别。
【解决方案7】:

这也有效,虽然它作弊

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]    

【讨论】:

  • 我已经修复了缩进,但我认为你应该用回车替换最后一个print。此外,无需将中间值存储在列表中,只需在迭代之间存储p
【解决方案8】:

如果你真的想在不导入任何你能做的事情的情况下让它成为一行:

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

但不要。

【讨论】:

  • 本质上非常Pythonic
  • ty 为 sol 不导入任何东西!
【解决方案9】:

我很惊讶没有人建议将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

【讨论】:

    【解决方案10】:

    我发现最快的方法是使用 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】:

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

    @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 装饰的性能时,这里可能会有一个技巧。我不确定上面的时间是否正确,所以我建议在你的系统上尝试一下,看看 numba 是否能提升性能。

    【讨论】:

      【解决方案12】:

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

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

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

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

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

      【讨论】:

        【解决方案13】:

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

        numpy.prod(lst)
        

        到目前为止是最快的解决方案(如果列表不是很短的话)。


        重现情节的代码:

        import perfplot
        import numpy
        
        import math
        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 math_prod(lst):
            return math.prod(lst)
        
        
        def itertools_accumulate(lst):
            for value in accumulate(lst, mul):
                pass
            return value
        
        
        b = perfplot.bench(
            setup=numpy.random.rand,
            kernels=[
                reduce_lambda,
                reduce_mul,
                forloop,
                numpy_prod,
                itertools_accumulate,
                math_prod,
            ],
            n_range=[2 ** k for k in range(20)],
            xlabel="len(a)",
        )
        b.save("out.png")
        b.show()
        

        【讨论】:

          【解决方案14】:

          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
          

          【讨论】:

            【解决方案15】:

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

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

            【讨论】:

              【解决方案16】:

              代码如下:

              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)
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2012-05-20
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多