Python groupby表现得很奇怪

时间:2014-08-29 16:42:11

标签: python itertools

from itertools import groupby

source = [ [1,2], [1,3], [2, 1] ]
gby = groupby(source, lambda x: x[0])

print 'as list'
for key, vals in list(gby):
    print 'key {}'.format(key)
    for val in vals:
        print '  val {}'.format(val)

print

print 'as iter'
gby = groupby(source, lambda x: x[0])
for key, vals in gby:
    print 'key {}'.format(key)
    for val in vals:
        print '  val {}'.format(val)

结果:

as list
key 1
key 2
  val [2, 1]

as iter
key 1
  val [1, 2]
  val [1, 3]
key 2
  val [2, 1]

list(gby)有什么问题?我希望list是纯函数,它如何设法破坏内部状态?

1 个答案:

答案 0 :(得分:4)

documentation记下了这一点:

  

返回的组本身就是一个共享底层的迭代器   可以使用groupby()进行迭代。因为源是共享的,所以   groupby()对象是高级的,前一个组不再可见。   因此,如果以后需要该数据,则应将其存储为列表:

groups = []
uniquekeys = []
data = sorted(data, key=keyfunc)
for k, g in groupby(data, keyfunc):
    groups.append(list(g))      # Store group iterator as a list
    uniquekeys.append(k)

在尝试迭代返回的组迭代器之前,您正在耗尽groupby对象(通过将其转换为列表),因此除了最后一个组之外的所有组都将丢失。

通过查看函数的Python实现,可以更容易地找到原因:

class groupby(object):
    # [k for k, g in groupby('AAAABBBCCDAABBB')] --> A B C D A B
    # [list(g) for k, g in groupby('AAAABBBCCD')] --> AAAA BBB CC D
    def __init__(self, iterable, key=None):
        if key is None:
            key = lambda x: x
        self.keyfunc = key
        self.it = iter(iterable)
        self.tgtkey = self.currkey = self.currvalue = object()
    def __iter__(self):
        return self
    def next(self):
        while self.currkey == self.tgtkey:
            self.currvalue = next(self.it)
            self.currkey = self.keyfunc(self.currvalue)
        self.tgtkey = self.currkey
        return (self.currkey, self._grouper(self.tgtkey))
    def _grouper(self, tgtkey):  # This is the "group" iterator
        while self.currkey == tgtkey:  # self.currkey != tgtkey if you advance groupby and then try to use this object.
            yield self.currvalue
            self.currvalue = next(self.it)
            self.currkey = self.keyfunc(self.currvalue)

调用next(groupby)将指向底层iterable(self.currvalue)的内部指针前进到下一个键,然后返回当前键(self.currkey)和_grouper迭代器。 _grouper将当前密钥作为参数(称为tgtkey),并将产生值(并重新计算self.currkey),直到self.currkeytgtkey不同为止,表示返回与当前键对应的所有值。因此,如果您在使用groupby对象之前提前_grouperself.currkey 永远不会等于tgtkey,那么_grouper迭代器将不返回任何内容。

如果由于某种原因你需要将groupby结果存储在列表中,你必须这样做:

gby_list = []
for key, vals in gby:
    gby_list.append(key, list(vals))

或者:

gby_list = [key, list(vals) for key, vals in gby]