用于累积字典值的Python生成器表达式

Hen*_*ton 6 python dictionary generator

生成器表达式抛弃了大量的元组对,例如.以列表形式:

pairs = [(3, 47), (6, 47), (9, 47), (6, 27), (11, 27), (23, 27), (41, 27), (4, 67), (9, 67), (11, 67), (33, 67)]
Run Code Online (Sandbox Code Playgroud)

对于成对的每一对,使用key = pair [0]和value = pair [1],我想将此对流提供给字典以累积地添加相应键的值.明显的解决方案是:

dict_k_v = {}
for pair in pairs:
    try:
        dict_k_v[pair[0]] += pair[1]
    except:
        dict_k_v[pair[0]] = pair[1]

>>> dict_k_v
{33: 67, 3: 47, 4: 67, 6: 74, 9: 114, 11: 94, 41: 27, 23: 27}
Run Code Online (Sandbox Code Playgroud)

但是,这可以通过生成器表达式或一些不使用for循环的类似构造来实现吗?

编辑

为了澄清,生成器表达式抛弃了大量的元组对:

(3,47),(6,47),(9,47),(6,27),(11,27),(23,27),(41,27),(4,67),(9) ,67),(11,67),(33,67)......

并且我希望在生成每对时将每个键值对累积到字典中(参见Paul McGuire的答案).pairs = list []语句是不必要的,对此感到抱歉.对于每对(x,y),x是整数,y可以是整数或小数/浮点数.

我的生成器表达式的形式如下:

((x,y) for y in something() for x in somethingelse())
Run Code Online (Sandbox Code Playgroud)

并希望将每个(x,y)对累积到defaultdict中.心连心.

Nik*_* B. 5

您可以使用元组解构和a defaultdict来大大缩短该循环:

from collections import defaultdict
d = defaultdict(int)
for k,v in pairs: d[k] += v
Run Code Online (Sandbox Code Playgroud)

它仍然使用for循环,但是您不必处理以前从未看到过密钥的情况。我认为这可能是最好的解决方案,无论是在可读性方面还是在性能方面。

概念证明使用 groupby

也就是说,您可以使用进行操作itertools.groupby,但这有点hack:

from collections import defaultdict
d = defaultdict(int)
for k,v in pairs: d[k] += v
Run Code Online (Sandbox Code Playgroud)

而且,这实际上应该比第一种方法的性能差,因为需要创建所有对的内存列表以进行排序。


Pau*_*McG 5

为了讨论,这里有一个简单的生成器函数来给我们一些数据:

from random import randint
def generator1():
    for i in range(10000):
        yield (randint(1,10), randint(1,100))
Run Code Online (Sandbox Code Playgroud)

这里是使用Python for循环来使用生成器并计算每个键值对的计数的基本解决方案

from collections import defaultdict

tally = defaultdict(int)
for k,v in generator1():
    tally[k] += v

for k in sorted(tally):
    print k, tally[k]
Run Code Online (Sandbox Code Playgroud)

将打印像:

1 49030
2 51963
3 51396
4 49292
5 51908
6 49481
7 49645
8 49149
9 48523
10 50722
Run Code Online (Sandbox Code Playgroud)

但我们可以创建一个协程,它将接受发送给它的每个键值对,并将它们全部累积到传递给它的defaultdict中:

# define coroutine to update defaultdict for every
# key,value pair sent to it
def tallyAccumulator(t):
    try:
        while True:
            k,v = (yield)
            t[k] += v
    except GeneratorExit:
        pass
Run Code Online (Sandbox Code Playgroud)

我们将使用tally defaultdict初始化协程,并通过向其发送None值来准备接受值:

# init coroutine
tally = defaultdict(int)
c = tallyAccumulator(tally)
c.send(None)
Run Code Online (Sandbox Code Playgroud)

我们可以使用for循环或列表推导将所有生成器值发送到协程:

for val in generator1():
    c.send(val)
Run Code Online (Sandbox Code Playgroud)

要么

[c.send(val) for val in generator1()]
Run Code Online (Sandbox Code Playgroud)

但相反,我们将使用零大小的双端队列来处理所有生成器表达式的值,而不创建一个不必要的临时列表:

# create generator expression consumer
from collections import deque
do_all = deque(maxlen=0).extend

# loop thru generator at C speed, instead of Python for-loop speed
do_all(c.send(val) for val in generator1())
Run Code Online (Sandbox Code Playgroud)

现在我们再看一下这些值:

for k in sorted(tally):
    print k, tally[k]
Run Code Online (Sandbox Code Playgroud)

我们得到另一个类似于第一个的列表:

1 52236
2 49139
3 51848
4 51194
5 51275
6 50012
7 51875
8 46013
9 50955
10 52192
Run Code Online (Sandbox Code Playgroud)

有关协同程序的更多信息,请访问David Beazley的页面:http://www.dabeaz.com/coroutines/

  • 这个答案编写得很好,但是很有动力:使用协程式生成器不会使用普通的生成器使用者为解决方案增加任何价值. (3认同)