由itertools groupby总和感到困惑

时间:2016-04-05 03:17:14

标签: python

考虑一下......

from itertools import groupby
from operator import itemgetter

data = [{'pid': 1, 'items': 1}, {'pid': 2, 'items': 5}, {'pid': 1, 'items': 3}]
data = sorted(data, key=itemgetter('pid'))

for pid, rows in groupby(data, lambda x: x['pid']):
    print(pid, sum(r['items'] for r in rows))
    for key in ['items']:
        print(pid, sum(r[key] for r in rows))

第一个print()调用打印正确的#,4表示pid 1,5表示2.第二个print()调用,在通过键列表的循环中,为两个打印0 。发生了什么事?

3 个答案:

答案 0 :(得分:4)

rows获得的groupby对象是一种只能被使用一次的生成器。当你为第一个print语句迭代它时,你会使用这些值,因此当你下次尝试迭代时,rows是一个空的生成器 - 你已经访问并用完了你的访问其迭代功能。

如果您希望项目在多次迭代过程中持久,您可以使用row_list = list(rows)然后使用row_list

为了更清楚,我建议将您的代码放入Python REPL并检查该循环中的type(rows),并查看该对象提供的API。

答案 1 :(得分:3)

你遇到了一个非常常见的生成器问题 - 它们只能迭代一次。 itertools作为规则返回生成器。

来自docs for groupby

  

返回的组本身就是一个迭代器,它与groupby()共享底层的iterable。由于源是共享的,因此当groupby()对象前进时,前一个组将不再可见。

只需移除您的print()个来电,然后观看即可。如果您需要多次访问返回的数据,列表是保存结果的潜在结构。

答案 2 :(得分:3)

固定代码:

from itertools import groupby
from operator import itemgetter

data = [{'pid': 1, 'items': 1}, {'pid': 2, 'items': 5}, {'pid': 1, 'items': 3}]
data = sorted(data, key=itemgetter('pid'))

for pid, rows_gen in groupby(data, lambda x: x['pid']):
    rows=list(rows_gen)      # save the group to access more than once
    print(pid, sum(r['items'] for r in rows))
    for key in ['items']:
        print(pid, sum(r[key] for r in rows))