返回清单的产品

问题:返回清单的产品

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

Is there a more concise, efficient or simply pythonic way to do the following?

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

EDIT:

I actually find that this is marginally faster than using 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())

gives me

('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

不使用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 |
------- + ----------- + ----------- + ----------- + ------ ----- +
 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 µs 216 µs

结果:np.prod如果np.array用作数据结构,则速度最快(小型阵列为18x,大型阵列为250x)

使用python 3.3.2:

       | 1 | 2 | 3 | 4 |
------- + ----------- + ----------- + ----------- + ------ ----- +
 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更慢吗?

Without using lambda:

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

it is better and faster. With 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)

In the following configuration:

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

Results with 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

Result: np.prod is the fastest one, if you use np.array as data structure (18x for small array, 250x for large array)

with 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

Is python 3 slower?


回答 1

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

回答 2

如果您的清单中只有数字:

from numpy import prod
prod(list)

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

if you just have numbers in your list:

from numpy import prod
prod(list)

EDIT: as pointed out by @off99555 this does not work for large integer results in which case it returns a result of type numpy.int64 while Ian Clelland’s solution based on operator.mul and reduce works for large integer results because it returns long.


回答 3

好吧,如果您真的想使其成为一行而不导入任何内容,则可以执行以下操作:

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

但是不要。

Well if you really wanted to make it one line without importing anything you could do:

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

But don’t.


回答 4

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

回答 5

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

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

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

import math

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

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

Starting Python 3.8, a prod function has been included to the math module in the standard library:

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

which returns the product of a start value (default: 1) times an iterable of numbers:

import math

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

Note that if the iterable is empty, this will produce 1 (or the start value if provided).


回答 6

我记得在comp.lang.python上进行了长时间的讨论(很抱歉,现在太懒了以至于无法生成指针),得出的结论是您的原始product()定义是最Pythonic的

请注意,建议不是要每次都编写for循环,而是编写一次函数(每种归约类型)并根据需要调用它!调用归约函数非常具有Python风格-可以很好地与生成器表达式配合使用,并且由于成功引入了sum(),Python保持了越来越多的内置归约函数-any()并且all()是最新添加的…

这个结论有点官方- reduce()从Python 3.0的内置删除了,说:

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

也可以看看 Python 3000中reduce()的命运,以获取来自Guido的支持引文(以及阅读该博客的Lispers的一些不那么支持的评论)。

PS,如果您偶然需要product()组合药,请参阅math.factorial()(新2.6)。

I remember some long discussions on comp.lang.python (sorry, too lazy to produce pointers now) which concluded that your original product() definition is the most Pythonic.

Note that the proposal is not to write a for loop every time you want to do it, but to write a function once (per type of reduction) and call it as needed! Calling reduction functions is very Pythonic – it works sweetly with generator expressions, and since the sucessful introduction of sum(), Python keeps growing more and more builtin reduction functions – any() and all() are the latest additions…

This conclusion is kinda official – reduce() was removed from builtins in Python 3.0, saying:

“Use functools.reduce() if you really need it; however, 99 percent of the time an explicit for loop is more readable.”

See also The fate of reduce() in Python 3000 for a supporting quote from Guido (and some less supporting comments by Lispers that read that blog).

P.S. if by chance you need product() for combinatorics, see math.factorial() (new 2.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

之所以走这条路,是因为在现实世界中,用这种方法可能会出现上溢或下溢的问题几乎是不可能的。(该计算结果越大,如果可以计算,则乘积将越大。)

The intent of this answer is to provide a calculation that is useful in certain circumstances — namely when a) there are a large number of values being multiplied such that the final product may be extremely large or extremely small, and b) you don’t really care about the exact answer, but instead have a number of sequences, and want to be able to order them based on each one’s product.

If you want to multiply the elements of a list, where l is the list, you can do:

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

Now, that approach is not as readable as

from operator import mul
reduce(mul, list)

If you’re a mathematician who isn’t familiar with reduce() the opposite might be true, but I wouldn’t advise using it under normal circumstances. It’s also less readable than the product() function mentioned in the question (at least to non-mathematicians).

However, if you’re ever in a situation where you risk underflow or overflow, such as in

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

and your purpose is to compare the products of different sequences rather than to know what the products are, then

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

is the way to go because it’s virtually impossible to have a real-world problem in which you would overflow or underflow with this approach. (The larger the result of that calculation is, the larger the product would be if you could calculate it.)


回答 8

我用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


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

I’ve tested various solutions with perfplot (a small project of mine) and found that

numpy.prod(lst)

is by far the fastest solution (if the list isn’t very short).


Code to reproduce the plot:

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


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

回答 9

令我惊讶的是,没有人建议itertools.accumulate与一起使用operator.mul。这样可以避免使用reduce,这与Python 2和3有所不同(由于functoolsPython 3需要导入),而且Guido van Rossum本人也认为它是非Python语言

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

I am surprised no-one has suggested using itertools.accumulate with operator.mul. This avoids using reduce, which is different for Python 2 and 3 (due to the functools import required for Python 3), and moreover is considered un-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

Example:

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

回答 10

一种选择是使用numba@jit@njit装饰器。我还对您的代码进行了一两个小调整(至少在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是否可以提高性能。

One option is to use numba and the @jit or @njit decorator. I also made one or two little tweaks to your code (at least in Python 3, “list” is a keyword that shouldn’t be used for a variable name):

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

For timing purposes, you need to run once to compile the function first using numba. In general, the function will be compiled the first time it is called, and then called from memory after that (faster).

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

Now when you execute your code, it will run with the compiled version of the function. I timed them using a Jupyter notebook and the %timeit magic function:

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)

Note that on my machine, running Python 3.5, the native Python for loop was actually the fastest. There may be a trick here when it comes to measuring numba-decorated performance with Jupyter notebooks and the %timeit magic function. I am not sure that the timings above are correct, so I recommend trying it out on your system and seeing if numba gives you a performance boost.


回答 11

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

The fastest way I found was, using 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])'))

and the timings are:

>>> using while : 0.8887967770060641

>>> using mul : 2.0838719510065857

>>> using lambda : 2.4227715369997895

回答 12

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

Python 3 result for the OP’s tests: (best of 3 for each)

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

尽管也有欺骗行为

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]    

This also works though its cheating

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]