简体   繁体   English

为什么zip()会丢弃我的生成器的值?

[英]Why does zip() drop the values of my generator?

I was writing an answer to this question when noticed that my simple implementation didn't produce correct results. 当我注意到我的简单实现没有产生正确的结果时,我正在写这个问题的答案。 While hunting down the bug, I noticed the following: 在追捕这个bug时,我注意到以下内容:

In [1]: import itertools
In [2]: gen = itertools.cycle((0,1,2))

In [3]: zip(gen, range(3))
Out[3]: [(0, 0), (1, 1), (2, 2)]

In [4]: zip(gen, range(3))
Out[4]: [(1, 0), (2, 1), (0, 2)]

For whatever reason, gen 's next() method is called one additioinal time. 无论出于何种原因, gennext()方法称为一个额外的时间。 To illustrate this, I used the following: 为了说明这一点,我使用了以下内容:

class loudCycle(itertools.cycle):
    def next(self):
        n = super(loudCycle, self).next()
        print n
        return n

In [6]: gen = loudCycle((0,1,2))
In [7]: zip(gen, range(3))
0
1
2
0
Out[7]: [(0, 0), (1, 1), (2, 2)]

This happens because zip evaluates iterators from left to right , meaning that, after three steps, it calls next() on gen and only then on iter(range(3)) (or something like that) and encounters a StopIteration . 发生这种情况是因为zip 从左到右评估迭代器,这意味着,在三个步骤之后,它在gen上调用next() ,然后在iter(range(3)) (或类似的东西next()上调用并遇到StopIteration To get around this, use the shorter (finite) iterable as the left-most argument: 要解决这个问题,请使用较短的(有限)迭代作为最左边的参数:

In [8]: zip(range(3), gen)
0
1
2
Out[8]: [(0, 0), (1, 1), (2, 2)]

Your self-answer is exactly right, and presents a very good solution -- if one of the arguments to zip is always shorter than the other. 你的自我答案是完全正确的,并提出了一个非常好的解决方案 - 如果 zip一个参数总是比另一个短。 However, in situations where you don't know which will be shorter, you might find islice useful. 但是,在您不知道哪个更短的情况下,您可能会发现islice很有用。 islice also provides an easy workaround if you want the first item in your tuples to be from your generator. 如果您希望元组中的第一项来自您的生成器, islice还提供了一种简单的解决方法。 In your case, you could do this: 在您的情况下,您可以这样做:

>>> import itertools
>>> gen = itertools.cycle(('a', 'b', 'c'))
>>> seq = range(3)
>>> zip(itertools.islice(gen, len(seq)), seq)
[('a', 0), ('b', 1), ('c', 2)]
>>> zip(itertools.islice(gen, len(seq)), seq)
[('a', 0), ('b', 1), ('c', 2)]

Your answer is probably better in this case -- it's certainly simpler -- but I thought I'd add this as a supplement. 在这种情况下你的答案可能更好 - 它当然更简单 - 但我想我会补充这个。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM