为什么' zip '从第一个迭代器中多吃一个元素?



我正在学习Effective Python(第二版)中的Item 76,我遇到了一个我不理解的案例。具体来说,我不明白为什么zip消耗了它的第一个参数的一个额外元素。考虑下面的代码:

l1 = []
l2 = [1]
l1_it = iter(l1)
l2_it = iter(l2)
test_it = zip(l2_it, l1_it)
_ = list(test_it):
try:
next(l2_it)
except StopIteration:
print('This should not happen')

这实际上打印了This should not happen,我发现这非常令人惊讶。我希望zip将其第一个参数留在仍然有一个元素要检索的状态。事实是,如果我使用zip(l1_it, l2_it)(也就是说,最短的列表是第一个),那么我确实可以调用next(l2_it)而不会触发异常。

这是预期的吗?

zip取最短可迭代对象的长度并限制在此范围内。由于l1没有条目,因此l2中的条目将不被处理。

然而…对于可迭代对象,Python不知道有多少项可用,所以它唯一的办法就是尝试获取一个项。如果没有项,它会得到一个异常。如果有一个元素,则它现在已经从迭代器中消耗了它。

也许你期待zip_longest的行为?https://docs.python.org/3/library/itertools.html?highlight=itertools itertools.zip_longest

说明iter()的行为:

>>> x = [1]
>>> x_iter = iter(x)
# We cannot get the length and unless we are prepared to fetch it,
# we cannot check if there are items available.
>>> len(x_iter)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: object of type 'list_iterator' has no len()
# First item can be fetched
>>> next(x_iter)
1
# There is no second item so Python raises a StopIteration
>>> next(x_iter)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration

我确实有这个问题,在阅读了这个和答案后,我明白了,为什么会发生这种情况,并简单地改变了压缩可迭代对象的顺序。一个在另一个(较短的可迭代对象)之前结束,然后是不应该被"吃掉"的那个;从一个(第一个视图)'unexpected'附加元素"作为第二,问题解决了。

所以基本上作为结论在我看来,人们应该记住总是把较短的iterable作为第一个参数,把较长的iterable作为第二个参数,以避免"surprise "

初版使用类似的例子:

>>> l1 = [1]                      # the shorter one for left side
>>> l2 = [2, 3]                   # the longer one for right side
>>> l1_it = iter (l1)
>>> l2_it = iter (l2)
>>> test_it = zip (l1_it, l2_it)  # zip now has stop iteration
# from l1_it and leaves l2_it
# untouched further
>>> print (list (test_it))        # go through test_it with list
[(1, 2)]                          # and print it
>>> next (l2_it)                  # as you can see here there is
3                                 # still a next (now as expected)

相关内容

  • 没有找到相关文章

最新更新