有没有更简洁,有效或简单的pythonic方法来执行以下操作?
1
2
3
4
5def product(list):
p = 1
for i in list:
p *= i
return p
编辑:
我实际上发现这比使用operator.mul快一点:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32from 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())
给我
1
2
3
4
5
6('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)
零结果不是很有趣。有趣的是,您在什么平台上使用哪个版本的Python。
否-我刚刚添加了不带零的数字,因为我意识到wisos的答案包括零,我想知道它产生了多少不同。我在ubuntu 9.10上使用python 2.6.4。
此处给出的选项之间存在功能差异,对于空白列表,reduce答案引发TypeError,而for循环答案返回1。这是for循环答案中的错误(乘积空列表中的数字最多不超过1,即17或犰狳)。
请尝试避免将内置名称(例如list)用作变量名称。
旧答案,但是我很想编辑,因此它不使用list作为变量名...
空列表的乘积为1。en.wikipedia.org/wiki/Empty_product
@ScottGriffiths您是否还会宣称空列表的总和不为0?我强烈不同意您的主张,一个空白清单的结果为1。
@分号:取决于空列表中元素的类型(即其定义不明确)。列表[a, b, c]的总和是字符串abc,因此您可以很好地争辩说空列表的总和应为(一个空字符串)。所以TypeError似乎对我来说合适-Python语言在这里似乎与我一致...
@ScottGriffiths我应该指定我的意思是数字列表。我要说的是,空列表的总和是该类型列表的+的标识元素(对于product / *同样如此)。现在,我意识到Python是动态类型的,这会使事情变得更难,但这是在使用Haskell这样的静态类型系统的理智的语言中解决的问题。但是Python仍然只允许sum对数字进行运算,因为sum([a, b])甚至不起作用,所以我再次说0对于产品的sum和1是有意义的。
@semicolon:我正在按照OP的样式进行求和:reduce(add, [a, b, c])确实有效(您也可以尝试a + b)。内置的sum被定义为仅适用于数字,因此,如果您假设列表中没有的项目类型,则可以对空列表求和。但是您也可以使用仅适用于字符串的方法对列表求和:a + b + c可以作为.join([a, b, c])来完成,与1 + 2 + 3可以作为sum([1, 2, 3])一样,但是在这种情况下,可以对空列表求和将给出一个空字符串,而不是零。
@ScottGriffiths我确实承认,在Python中,由于缺少类型系统,事情变得更加复杂,因此我猜对空列表而言,它失败将是您因没有静态类型而付出的代价。在Haskell中,这是诸如mempty,empty之类的问题已解决的问题,并且1和0是多态的。我更争辩说,即使从实际意义上讲,像TypeError之类的东西或需要一个起始值,"数学上"一个空列表的乘积是1或"该元素的乘法身份",在Python中。
如何与Python一起将列表中的所有项目相乘?
不使用lambda:
1
2from operator import mul
reduce(mul, list, 1)
更好,更快。使用python 2.7.5
1
2
3
4
5
6
7
8
9
10from 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)
在以下配置中:
1
2
3
4a = 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
5
6| 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.array用作数据结构,则np.prod是最快的(小数组为18x,大数组为250x)
使用python 3.3.2:
1
2
3
4
5
6| 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更慢吗?
非常有趣,谢谢。知道为什么python 3可能会更慢吗?
可能的原因:(1)Python 3 int是Python 2 long。 Python 2将使用" int"直到溢出32位; Python 3从一开始就将使用" long"。 (2)Python 3.0是"概念证明"。尽快升级到3.1!
Ive在另一台机器上重做了相同的测试:python 2.6(带有lambda :, 21.843887090682983)(没有lambda :, 9.7096879482269287)python 3.1:有lambda:24.7712180614没有lambda:10.7758350372
都失败,并带有空列表。
@bug:谢谢,固定
@RuggeroTurra理论:随着时间的流逝,Python 3.x的速度将会提高。证明:您的个人资料时间。
如果列表为空,则reduce(mul, list)失败...相反,return reduce(mul, list) if list else 1呢?
我尝试使用numpy prod(list(range(5,101)))并产生0,为什么呢?任何人都可以重现此结果吗?这是使用Anaconda安装的Python 3。
由于溢出,请尝试np.prod(range(1, 66)) * 66。要解决它,请使用np.prod(np.arange(1L, 101L, dtype=np.object))
请注意,您必须从Python 3中的functools模块导入reduce运算符。 from functools import reduce。
1reduce(lambda x, y: x * y, list, 1)
+1,但请参见@wisos关于operator.mul的答案,以获得更好的方法。
为什么operator.mul比x * y更可取?
operator.mul是一个函数,因此不仅可以替换x * y,而且可以替换整个lambda表达式(即reduce的第一个参数)
您必须执行导入from functools import reduce使其在Python 3中工作。
如果您的清单中只有数字:
1
2from numpy import prod
prod(list)
编辑:@ off99555指出,这不适用于大整数结果,在这种情况下,它返回类型为numpy.int64的结果,而基于operator.mul和reduce的伊恩·克莱尔兰德(Ian Clelland)的解决方案适用于大整数结果,因为它返回< x12>。
如果列表很短,这会比较慢
我尝试评估from numpy import prod; prod(list(range(5,101)))并输出了0,可以在Python 3上重现此结果吗?
因为在这种情况下prod返回类型为numpy.int64的结果,并且您已经为range(5,23)获得了溢出(实际上是负值)。对大整数使用基于operator.mul和reduce的@Ian Clellands解决方案(在这种情况下,它返回long似乎具有任意精度)。
@ off99555两种解决方案:或者通过执行np.prod(np.arange(5.0,101.0))从浮点类型列表开始,或者通过执行np.prod(np.array(range(5,101)).astype(np.float64))将其转换为浮点类型。请注意,NumPy使用np.float64而不是float。我不知道区别。
1
2import operator
reduce(operator.mul, list, 1)
最后一个论点(1)真的必要吗?
如果列表可能为空,则最后一个参数是必需的,否则它将引发TypeError异常。当然,有时候您会想要一个例外。
对我来说,它不带该参数就返回0,因此您也可以考虑强制执行空产品约定。
或python3中的functools.reduce(..)
好吧,如果您真的想使其成为一行而不导入任何内容,则可以执行以下操作:
1eval('*'.join(str(item) for item in list))
但是不要。
现在,让我们传递一个有趣的字符串列表(以强调"不"的原因)...
我记得在comp.lang.python上进行了很长时间的讨论(很抱歉,现在太懒了以至于无法生成指针),这些结论得出结论,您最初的product()定义是最Python的。
请注意,建议不是要每次都编写for循环,而是编写一次函数(每种归约类型)并根据需要调用它!调用归约函数非常具有Python风格-可以很好地与生成器表达式配合使用,并且由于sum()的成功引入,Python越来越多地内置了归约函数-any()和all()是最新添加的...
这个结论有点正式-从Python 3.0的内置函数中删除了reduce(),说:
"Use functools.reduce() if you really need it; however, 99 percent of the time an explicit for loop is more readable."
另请参见Python 3000中reduce()的命运,以获取来自Guido的支持引文(以及阅读该博客的Lispers的一些不那么支持的评论)。
附言如果偶然需要组合product(),请参见math.factorial()(新2.6)。
+1(就我所知)准确地解释了Python社区中的普遍情绪-虽然我绝对喜欢在这种情况下与所说的普遍情绪背道而驰,但无论如何,最好了解它们。另外,我喜欢LtU不支持Lispers的功能(我想,Id就是其中之一)。 :-)
从Python 3.8开始,prod函数已包含在标准库的math模块中:
math.prod(iterable, *, start=1)
它返回start值(默认值:1)乘以可迭代数字的乘积:
1
2
3import math
math.prod([2, 3, 4]) # 24
请注意,如果iterable为空,则将生成1(或start值(如果提供))。
这个答案的目的是提供一种在某些情况下有用的计算方法,即当a)大量数值相乘而最终产品可能非常大或非常小,并且b)您不这样做时真正关心的是确切的答案,但是有许多序列,并且希望能够根据每个人的产品订购它们。
如果要乘以列表的元素(其中l是列表),则可以执行以下操作:
1
2import math
math.exp(sum(map(math.log, l)))
现在,这种方法不像
1
2from operator import mul
reduce(mul, list)
如果您是不熟悉reduce()的数学家,则情况可能恰恰相反,但我不建议在正常情况下使用它。它也比问题中提到的product()函数可读性差(至少对非数学家而言)。
但是,如果您曾经面临下溢或上溢的风险,例如
1
2>>> reduce(mul, [10.]*309)
inf
您的目的是比较不同序列的产品,而不是了解产品是什么,然后
1
2>>> sum(map(math.log, [10.]*309))
711.49879373515785
之所以走这条路,是因为在现实世界中,用这种方法可能会出现上溢或下溢的问题几乎是不可能的。 (该计算的结果越大,如果可以计算,则乘积将越大。)
它很聪明,但是如果您有任何负值或零值,它都会失败。 :/
我用perfplot(我的一个小项目)测试了各种解决方案,发现
1numpy.prod(lst)
是迄今为止最快的解决方案(如果列表不是很短)。
复制剧情的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48import 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,
)
我很惊讶,没有人建议将itertools.accumulate与operator.mul一起使用。这样可以避免使用reduce,这与Python 2和3有所不同(由于Python 3需要导入functools),此外,Guido van Rossum本人也认为它是非Python语言的:
1
2
3
4
5
6
7from itertools import accumulate
from operator import mul
def prod(lst):
for value in accumulate(lst, mul):
pass
return value
例:
1
2prod([1,5,4,3,5,6])
# 1800
一种选择是使用numba和@jit或@njit装饰器。我还对您的代码进行了一两个小调整(至少在Python 3中,"列表"是一个不应用于变量名的关键字):
1
2
3
4
5
6@njit
def njit_product(lst):
p = lst[0] # first element
for i in lst[1:]: # loop over remaining elements
p *= i
return p
出于计时目的,您需要先运行一次以使用numba编译函数。通常,该函数将在首次调用时进行编译,然后在内存中调用(更快)。
1njit_product([1, 2]) # execute once to compile
现在,当您执行代码时,它将与函数的编译版本一起运行。我使用Jupyter笔记本和%timeit魔术功能为它们计时:
1
2
3
4
5product(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是否可以提高性能。
我发现最快的方法是使用while:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34mysetup = '''
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])'))
时间是:
1
2
3
4
5>>> using while : 0.8887967770060641
>>> using mul : 2.0838719510065857
>>> using lambda : 2.4227715369997895
这可能是由于列表的长度较短,可能需要更多的实验
这也可以通过欺骗来实现
1
2
3
4
5
6
7
8
9def 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。
阶乘?列表的乘积不是阶乘。...