在Python生成器表达式中过滤数值

6

我有一个字典dct,我希望对其中的每个值进行求和,前提是它们对应的键存在于指定列表lst中。

目前我使用的代码如下:

sum(dct[k] for k in lst)

在上述生成器表达式中,我希望处理KeyError,以防列表中的键在字典中找不到。我无法找到如何在此生成器表达式中实现(语法方面)try-except方法或if-else方法。
如果列表中的键未在字典中找到,则应继续获取其他值。总和的最终结果不应受任何缺失键的影响。如果没有键存在,则总和的结果应为零。

1
你不能在生成器表达式中使用 try。为什么不使用 dct.get(k),如果未找到键,则会返回 None 而不是错误?如果未找到键,您的代码的正确行为是什么(例如,应将其视为 0)? - jonrsharpe
如果找不到一个键,那么应该继续处理剩下的键。(我会更新我的描述来解释这种情况 - 感谢您注意到这一点) - Yannis
5个回答

7

好的,有几种选项,首选是使用 dict.get()

# 1
sum(dct.get(k, 0) for k in lst)
# 2
sum(dct[k] for k in lst if k in dct)

另一个选项是在迭代 lst 之前对其进行过滤:

sum(dct[k] for k in filter(lambda i: i in dct, lst))

你可以使用 reduce 函数 作为 sum 的替代品来处理过滤后的列表:

reduce(lambda a, k: a + dct[k], filter(lambda i: i in dct, lst))

现在让我们使用timeit找到最快的方法:

from timeit import timeit
import random

lst = range(0, 10000)
dct = {x:x for x in lst if random.choice([True, False])}

via_sum = lambda:(sum(dct.get(k, 0) for k in lst))
print("Via sum and get: %s" % timeit(via_sum, number=10000))
# Via sum and get: 16.725695848464966

via_sum_and_cond = lambda:(sum(dct[k] for k in lst if k in dct))
print("Via sum and condition: %s" % timeit(via_sum_and_cond, number=10000))
# Via sum and condition: 9.4715681076

via_reduce = lambda:(reduce(lambda a, k: a + dct[k], filter(lambda i: i in dct, lst)))
print("Via reduce: %s" % timeit(via_reduce, number=10000))
# Via reduce: 19.9522120953

因此,在生成器表达式中使用if语句对项进行求和是最快的选择。
sum(dct[k] for k in lst if k in dct) # Via sum and condition: 9.4715681076

祝你好运!


1
非常完整的答案。不知道您是否能够提供一些有关比较这4个选项的见解,特别是第三个选项与前两个选项的比较。它们之间是否存在任何效率优势或更“Pythonic”的方法? - Yannis
@Yannis 谢谢,当然)请查看更新的答案,其中包含时间度量:) 我猜赢家方法最符合“Pythonic”的风格。 - Andriy Ivaneyko

5
您有两个选项:
1. 检查键是否存在。 2. 检查值是否为null或undefined。
sum(dct[k] for k in lst if k in dct)

或者使用get
sum(dct.get(k, 0) for k in lst)

dct.get(k, 0)是一个用于Python字典的方法,如果k是字典dct的键,则返回对应的值dct[k],否则返回默认值0


2
sum(dct[k] for k in lst if k in dct)

确实是有帮助的答案;其他人也提到了这种方法,从我所看到的来看,你的方法并不是最快的。抱歉。 - Yannis

1
你可以简单地使用.get()尝试从字典中获取键的值,如果未找到,则返回None或您提供的默认参数,在本例中为0。
>>> dct = {1:2, 3:4, 5:6}
>>> lst = [1,5]
>>> sum(dct.get(k, 0) for k in lst)
8    

如果一些(或全部)键不存在,总和仍将正常运行。
>>> lst = [10, 11]
>>> sum(dct.get(k, 0) for k in lst)
0

1
您可以使用字典的get方法,在未找到时提供默认值:
sum(dct.get(k, 0) for k in lst)

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