如何使用列表推导来模拟sum()函数?

56

是否可以使用列表推导式模拟类似于sum()的函数?

例如 - 我需要计算列表中所有元素的乘积:

list = [1, 2, 3]
product = [magic_here for i in list]

#product is expected to be 6

做同样事情的代码:

def product_of(input):
   result = 1
   for i in input:
      result *= i
   return result

可能是 https://dev59.com/G3I95IYBdhLWcg3w5SSh 的重复问题。 - Tomerikoo
OP正在寻找https://dev59.com/Bm4NtIcB2Jgan1znTjL2#70558247和https://dev59.com/G3I95IYBdhLWcg3w5SSh#54697535的组合:`prod(i for i in list)`。 - user2987828
10个回答

52
>>> from functools import reduce
>>> from operator import mul
>>> nums = [1, 2, 3]
>>> reduce(mul, nums)
6

Python 3 Hack

对于这种做法,例如[total := total + x for x in [1, 2, 3, 4, 5]],这是一个可怕的想法。使用列表推导式模拟 sum() 的一般思路违背了列表推导式的整个目的。在这种情况下不应该使用列表推导式。

Python 2.5 / 2.6 Hack

在 Python 2.5 / 2.6 中,您可以使用vars()['_[1]']来引用当前正在构建的列表推导式。这是可怕的,绝不能使用,但它是最接近您在问题中提到的(使用列表推导式模拟乘积)。

>>> nums = [1, 2, 3]
>>> [n * (vars()['_[1]'] or [1])[-1] for n in nums][-1]
6

4
哎呀,那只是......我甚至不知道。 - joneshf
2
这其实很不错...我不知道你可以这样做(也不知道何时或为什么想要这样做)...但还是很酷的。 - Joran Beasley
1
你的方法比我的更加巧妙,点赞!;-) - Patrick

52
不行;列表推导式生成的列表长度与其输入相同。你需要使用Python的其他函数工具(特别是在这种情况下使用reduce())将序列折叠成单个值。

3
Python 3 中的 functools 模块。 - xealits

45

Python 3.8 开始,引入了 赋值表达式(PEP 572) (:= 运算符),我们可以在列表推导式中使用和递增变量,从而将列表减少为其元素的总和:

total = 0
[total := total + x for x in [1, 2, 3, 4, 5]]
# total = 15

这段代码:

  • 初始化一个变量total0
  • 对于每个项目,通过赋值表达式将当前循环的项加到total中(total := total + x

4
[total := total + x for x in [1, 2, 3, 4, 5]][-1] 的结果是求和(15),需要取最后一个元素。 - Léo Chaz Maltrait

16
列表推导式总是创建另一个列表,因此在组合它们时不太有用(例如,为了给出一个单一的数字)。此外,除非你非常狡猾,否则无法在列表推导式中进行赋值。
我认为只有当您只想在列表中包含特定值,或者没有数字列表时,使用列表推导作为sum方法才有用。
list = [1,2,3,4,5]
product = [i for i in list if i % 2 ==0] # only sum even numbers in the list
print sum(product)

另一个例子:

# list of the cost of fruits in pence
list = [("apple", 55), ("orange", 60), ("pineapple", 140), ("lemon", 80)]
product = [price for fruit, price in list]
print sum(product)

在列表推导式中创建分配方式的超级隐蔽方法。
dict = {"val":0}
list = [1, 2, 3]
product = [dict.update({"val" : dict["val"]*i}) for i in list]
print dict["val"] # it'll give you 6!

...但那太糟糕了 :)


9

类似这样的:

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

3
我认为你的意思是 x+y 而不是 x*y... 尽管对于你的测试数据来说这两者都会得到相同的结果。 - Greg Ennis

6
我将补充Ignacio Vazquez-Abrams的回答,提供一些使用Python的reduce运算符的代码。
list_of_numbers = [1, 5, 10, 100]
reduce(lambda x, y: x + y, list_of_numbers)

这也可以写成

list_of_numbers = [1, 5, 10, 100]

def sum(x, y):
    return x + y

reduce(sum, list_of_numbers)

奖励:Python在内置的sum函数中提供了此功能。在我看来,这是最易读的表达式。

list_of_numbers = [1, 5, 10, 100]
sum(list_of_numbers)

4
>>> reduce(int.__mul__,[1,2,3])
6

C:\Users\Henry>python -m timeit -s "" "reduce(int.__mul__,range(10000))" 
1000 loops, best of 3: 910 usec per loop

C:\Users\Henry>python -m timeit -s "from operator import mul" "reduce(mul,range(10000))"
1000 loops, best of 3: 399 usec per loop

C:\Users\Henry>

0

可以使用带有列表推导式的lambda来实现。 由于我们无法在列表推导式中分配值,因此我们使用lambda。

解决方案:

>>> (lambda number_list, sum=0:[sum for number in number_list for sum in [sum + number]][-1])([1, 2, 3, 4, 5])
>>> 15

0

我可能有点晚参与这个讨论,但我想提一下列表推导式是图灵完备的,因此可以使用列表推导式来完成这个任务!

然而,这种方法比较混乱,所以我使用了以下技巧,它可以创建一个累积数组,并返回最后一个元素。

def sum(l):
    return [c[-1] for c in [[0]] for e in l if c.append(c[-1] + e) is None][-1]

-2

http://code.activestate.com/recipes/436482/上找到了魔法。

>>> L=[2, 3, 4]
>>> [j for j in [1] for i in L for j in [j*i]][-1]
24

代码应该像以下的逻辑。

L=[2, 3, 4]
P=[]
for j in [1]:
    for i in L:
        for j in [j*i]:
            P.append(j)
print(P[-1])

2
这被标记为VLQ。构建整个列表,然后只取一个值 - 因此极其低效且技术上不可能“用列表推导来模拟”(由于顶部答案中所述的原因)。这可以作为“反例”,但它太糟糕了,我倾向于建议删除。 - ivan_pozdeev
只是不推荐这种方法,而且答案甚至不正确。出于好奇心,我用以下代码使其正常工作:[j for j in [0] for i in L for j in [j+i]][-1]但我绝不会推荐这种方法。要进行求和,您需要有一个起始列表,其中包含一个标识为[0]的元素。对于乘积,从[1]开始可以工作,因为您将第一个数字乘以1,因此需要更改为[0]。正如@ivan_pozdeev所指出的那样,这是使用操作应用于列表的每个元素来构建新列表,然后仅获取最终值。这与问题不同。 - Ryan Beesley
这是我的旧帖子,eval('*'.join(map(str,list))) 是我通常使用的方法。 - graviton

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