Python 迭代器和 zip

2022-01-10 00:00:00 python zip iterator

问题描述

使用 x = [1,2,3,4],我可以从 i = iter(x) 获得一个迭代器.

With x = [1,2,3,4], I can get an iterator from i = iter(x).

有了这个迭代器,我可以使用 zip 函数创建一个包含两个项目的元组.

With this iterator, I can use zip function to create a tuple with two items.

>>> i = iter(x)
>>> zip(i,i)
[(1, 2), (3, 4)]

即使我可以使用这种语法来获得相同的结果.

Even I can use this syntax to get the same results.

>>> zip(*[i] * 2)
[(1, 2), (3, 4)]

这是如何工作的?zip(i,i)zip(*[i] * 2) 的迭代器如何工作?

How does this work? How an iterator with zip(i,i) and zip(*[i] * 2) work?


解决方案

迭代器就像一个项目流.您一次只能查看一个流中的项目,并且您只能访问第一个元素.要查看流中的某些内容,您需要将其从流中移除,一旦您从流顶部取出某些内容,它就会从流中永久消失.

An iterator is like a stream of items. You can only look at the items in the stream one at a time and you only ever have access to the first element. To look at something in the stream, you need to remove it from the stream and once you take something from the top of the stream, it's gone from the stream for good.

当您调用 zip(i, i) 时,zip 首先查看第一个流并取出一个项目.然后它查看第二个流(恰好与第一个流相同的流)并取出一个项目.然后它从这两个项目中创建一个元组,并一遍又一遍地重复此操作,直到流中没有任何内容.

When you call zip(i, i), zip first looks at the first stream and takes an item out. Then it looks at the second stream (which happens to be the same stream as the first one) and takes an item out. Then it makes a tuple out of those two items and repeats this over and over until there is nothing left in the stream.

也许更容易看出我是否要在纯 python 中编写 zip 函数(为简单起见,只有 2 个参数).它看起来像1:

Maybe it's easier to see if I were to write the zip function in pure python (with only 2 arguments for simplicity). It would look something like1:

def zip(a, b):
    out = []
    try:
        while True:
            item1 = next(a)
            item2 = next(b)
            out.append((item1, item2))
    except StopIteration:
        return out

现在想象一下您所讨论的情况,其中 ab 是同一个对象.在这种情况下,我们只需在迭代器上调用 next 两次(在您的示例中为 i),它将只从 i 中获取前两项按顺序将它们打包成一个元组.

Now imagine the case that you are talking about where a and b are the same object. In that case, we just call next twice on the iterator (i in your example case) which will just take the first two items from i in sequence and pack them into a tuple.

一旦我们了解了 zip(i, i) 的行为方式为什么会这样,zip(*([i] * 2)) 就不一样了难的.让我们从内到外阅读表达式...

Once we've understood why zip(i, i) behaves the way it does, zip(*([i] * 2)) isn't too hard. Lets read the expression from the inside out...

[i] * 2

这只是创建了一个新列表(长度为 2),其中两个元素都是对迭代器 i 的引用.所以它和 zip(*[i, i]) 是一样的(当你想重复超过 2 次的时候写起来更方便).* unpacking 是 python 中的一个常见习惯用法,您可以在 python 教程.它的要点是 python 获取可迭代对象并解包"它,就好像可迭代对象的每个项目都是函数的单独位置参数一样.所以:

That just creates a new list (of length 2) where both of the elements are references to the iterator i. So it's the same thing as zip(*[i, i]) (it's just more convenient to write when you want to repeat something many more than 2 times). * unpacking is a common idiom in python and you can find more information in the python tutorial. The gist of it is that python takes the iterable and "unpacks" it as if each item of the iterable was a separate positional argument to the function. So:

zip(*[i, i])

做同样的事情:

zip(i, i)

现在鲍勃是我们的叔叔了.我们刚刚开始讨论,因为 zip(i, i) 是本次讨论的起点.

And now Bob's our uncle. We've just come full-circle since zip(i, i) is where this discussion started.

1这个示例代码绝对比前面提到的只接受 2 个参数更简单.例如,zip 可能会在输入参数上调用 iter 以便它适用于任何可迭代(不仅仅是迭代器),但这应该足以说明问题跨越...

1This example code is definitely simplified more than just the afore-mentioned only accepting 2 arguments. For example, zip is probably going to call iter on the input arguments so that it works for any iterable (not just iterators), but this should be enough to get the point across...

相关文章