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
是纯函数,它如何设法破坏内部状态?
答案 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.currkey
与tgtkey
不同为止,表示返回与当前键对应的所有值。因此,如果您在使用groupby
对象之前提前_grouper
,self.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]