8

试图想出一个单线来实现以下目标(对一个键的所有值求和):

>>> 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、collections 等。

 { tup[0] : SELF_REFERENCE.setdefault(tup[0],0) + tup[1]  for tup in data }

在 Python 中是否可以使用对当前正在理解的对象的引用?如果没有,是否有任何方法可以在不使用任何导入的情况下以单行方式实现这一点,即使用基本的列表/字典理解和内置函数。

4

4 回答 4

10

不,那里没有。dict 理解为每次迭代生成一个项目,您的代码需要生成更少的项目(合并值)。

没有办法访问在早期迭代中生成的密钥,而不是使用(丑陋的,unpythonic)副作用技巧。由推导式生成的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))}
于 2014-01-14T11:49:23.213 回答
2

这几乎就像你正在尝试做的事情。但我不会推荐这个,因为可读性会受到影响。

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}
于 2014-01-14T12:25:14.817 回答
2

不要使用单线笔。而是使用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 且快速。

于 2014-01-14T12:09:42.707 回答
2

使用reducecollections.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})
于 2014-01-14T11:59:20.127 回答