这是观察到的行为:
In [4]: x = itertools.groupby(range(10), lambda x: True)
In [5]: y = next(x)
In [6]: next(x)
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-6-5e4e57af3a97> in <module>()
----> 1 next(x)
StopIteration:
In [7]: y
Out[7]: (True, <itertools._grouper at 0x10a672e80>)
In [8]: list(y[1])
Out[8]: [9]
的预期输出list(y[1])
为[0,1,2,3,4,5,6,7,8,9]
这里发生了什么?
我观察到了这一点cpython 3.4.2
,但其他人通过cpython 3.5
和发现了这一点IronPython 2.9.9a0 (2.9.0.0) on Mono 4.0.30319.17020 (64-bit)
。
在Jython 2.7.0
和pypy上观察到的行为:
Python 2.7.10 (5f8302b8bf9f, Nov 18 2015, 10:46:46)
[PyPy 4.0.1 with GCC 4.8.4]
>>>> x = itertools.groupby(range(10), lambda x: True)
>>>> y = next(x)
>>>> next(x)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>> y
(True, <itertools._groupby object at 0x00007fb1096039a0>)
>>>> list(y[1])
[]
itertools.groupby
文档告诉我们
itertools.groupby(iterable, key=None)
[...]
的操作
groupby()
类似于Unix中的uniq过滤器。每当键函数的值更改时,它都会生成一个中断或新组(这就是为什么通常需要使用相同的键函数对数据进行排序的原因)。这种行为与SQL的GROUP BY不同,后者的GROUP BY聚集公共元素,而不管它们的输入顺序如何。返回的组本身就是一个与共享底层可迭代对象的迭代器
groupby()
。因为源是共享的,所以当`groupby()“对象前进时,先前的组不再可见。因此,如果以后需要该数据,则应将其存储为列表[-]
因此,从最后一段开始的假设是[]
,由于迭代器已经前进并满足,因此生成的列表将是空列表StopIteration
。但是在CPython中结果却令人惊讶[9]
。
这是因为_grouper
迭代器在原始迭代器之后滞后了一个项目,这是因为groupby
需要先窥视一个项目以查看它是否属于当前组或下一个组,但它必须能够稍后将其作为第一个项目生成。新组。
然而,currkey
和currvalue
属性groupby
都没有当复位原来迭代器被耗尽,所以currvalue
点还是从迭代器的最后一个项目。
CPython文档实际上包含此等效代码,并且其行为与C版本代码完全相同:
class groupby:
# [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) # Exit on StopIteration
self.currkey = self.keyfunc(self.currvalue)
self.tgtkey = self.currkey
return (self.currkey, self._grouper(self.tgtkey))
def _grouper(self, tgtkey):
while self.currkey == tgtkey:
yield self.currvalue
try:
self.currvalue = next(self.it)
except StopIteration:
return
self.currkey = self.keyfunc(self.currvalue)
值得注意的是,__next__
发现找到下一组的第一项,并将其密钥存储到中,self.currkey
并将其值存储到中self.currvalue
。但关键是线
self.currvalue = next(self.it) # Exit on StopIteration
当next
引发时StopItertion
,self.currvalue
静止图像仍包含前一组的最后一个键。现在,当y[1]
变成a时list
,它首先产生的值self.currvalue
,然后才next()
在基础迭代器上运行(并StopIteration
再次遇到)。
即使文档中有等效的Python,其行为也与CPython中权威的C代码实现完全一样,IronPython,Jython和PyPy会给出不同的结果。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句