如何在一个行分组器中复制或重用迭代器?

时间:2018-08-09 10:41:49

标签: python iterator copy

我正在尝试在Python中以单行表达式进行分组。我想建立一个dict的组和组中的项目数:

{k: {'objects': list(g), 'count': len(list(g))}
        for k,g in groupby(rows, key=lambda x: x['group_id'])}

但是g是一个迭代器,不能与'count': len(list(g))一起使用。 如何在一个行表达式中计数和重用g

1 个答案:

答案 0 :(得分:4)

您不能多次在迭代器上调用list(),否。您必须先存储结果。

根据可行性,您的选择是:

  • 不使用单线。使用常规的for循环,然后将list()的结果首先分配给一个单独的变量。
  • groupby()迭代器包装在将list()应用于组对象的生成器表达式中。
  • 使用单个元素元组添加第二个循环,即list()调用,因此您可以将循环目标用作要构建的字典中两个键的变量。
  • 等到Python 3.8添加PEP 572 assignment expressions并将list()调用结果分配给一个名称以供len()重用之前,

第一个应该是首选选项。可读性很重要!

result = {}
for group_id, group in groupby(rows, key=lambda x: x['group_id']):
    objects = list(group)
    result[group_id] = {'objects': objects, 'count': len(objects)}

使用生成器表达式可能是下一个最佳选择:

list_group = ((k, list(g)) for k, g in groupby(rows, key=lambda x: x['group_id']))
result = {k: {'objects': gl, 'count': len(gl)} for k, gl in list_group}

生成器表达式循环在for k, gl in list_group迭代时并行执行。

第二个循环选项如下:

{
    k: {'objects': gl, 'count': len(gl)}
    for k, g in groupby(rows, key=lambda x: x['group_id'])
    for gl in (list(g),)
}

由于此技巧令人惊讶且难以阅读,因此强烈建议您不要使用它。

在Python 3.8中,实现了PEP 572的您可以使用:

{
    k: {'objects': gl := list(g), 'count': len(gl)}
    for k, g in groupby(rows, key=lambda x: x['group_id'])
}

可以使用itertools.tee() object将迭代器“加倍”,但这必须将整个列表分别缓存在内存中,使内存成本加倍,并且代码将变得不再可读(因为您必须使用类似的技巧然后将tee()调用迭代器也存储在变量中!)。