我正在尝试想出一种单行代码来实现以下目标(对键的所有值求和):
>>> data = [('a', 1), ('b', 3), ('a', 4), ('c', 9), ('b', 1), ('d', 3)]
>>> res = {}
>>> for tup in data:
... res[tup[0]] = res.setdefault(tup[0], 0) + tup[1]
...
>>> res
{'a': 5, 'c': 9, 'b': 4, 'd': 3}
单行版本,不使用任何导入,如 itertools、集合等。
{tup[0]: SELF_REFERENCE.setdefault(tup[0], 0) + tup[1] for tup in data}
Python 中是否可以使用当前正在理解的对象的引用?
如果没有,有没有什么方法可以在不使用任何导入的情况下以单行实现这一点,即使用基本的列表/字典理解和内置函数?
不,没有。字典理解会为每次迭代生成一个new项目,并且您的代码需要生成较少项目(合并值)。
如果不使用(丑陋的、非Pythonic的)副作用技巧,就无法访问早期迭代中生成的密钥。理解将要生成的
dict
对象尚不存在,因此也无法生成自引用。
只需坚持你的
for
循环,它的可读性要强得多。
另一种方法是使用排序和分组,O(NlogN) 算法与直接循环的简单 O(N) 算法:
from itertools import groupby
from operator import itemgetter
res = {key: sum(t[1] for t in group)
for key, group in groupby(sorted(data, key=itemgetter(0)), key=itemgetter(0))}
reduce
和 collections.Counter
:
>>> from operator import add
>>> from collections import Counter
>>> reduce(add, (Counter(dict([x])) for x in data))
Counter({'c': 9, 'a': 5, 'b': 4, 'd': 3})
请勿使用眼线笔。相反,使用
collections.defaultdict
和一个简单的 for 循环:
>>> pairs = [('a', 1), ('b', 3), ('a', 4), ('c', 9), ('b', 1), ('d', 3)]
>>> result = defaultdict(int)
>>> for key, value in pairs:
... result[key] += value
...
>>> result
defaultdict(<class 'int'>, {'a': 5, 'c': 9, 'b': 4, 'd': 3})
它很容易理解,Pythonic且快速。
这几乎就像您正在尝试做的事情。但我不会推荐这个,因为可读性会受到影响。
data = [('a',1),('b',3),('a',4),('c',9),('b',1),('d',3)]
print reduce(lambda d,i: [d.__setitem__(i[0],d.get(i[0],0)+i[1]),d][1], data, {})
输出
{'a': 5, 'c': 9, 'b': 4, 'd': 3}