问题:zip(* [iter(s)] * n)在Python中如何工作?

s = [1,2,3,4,5,6,7,8,9]
n = 3

zip(*[iter(s)]*n) # returns [(1,2,3),(4,5,6),(7,8,9)]

zip(*[iter(s)]*n)工作如何?如果用更冗长的代码编写,它将是什么样?

s = [1,2,3,4,5,6,7,8,9]
n = 3

zip(*[iter(s)]*n) # returns [(1,2,3),(4,5,6),(7,8,9)]

How does zip(*[iter(s)]*n) work? What would it look like if it was written with more verbose code?


回答 0

是序列上的迭代器。[x] * n产生一个包含n数量的列表x,即一个长度的列表n,其中每个元素都是x*arg将序列解压缩为函数调用的参数。因此,您要向传递3次相同的迭代器zip(),并且每次都会从迭代器中提取一个项目。

x = iter([1,2,3,4,5,6,7,8,9])
print zip(x, x, x)

is an iterator over a sequence. [x] * n produces a list containing n quantity of x, i.e. a list of length n, where each element is x. *arg unpacks a sequence into arguments for a function call. Therefore you’re passing the same iterator 3 times to zip(), and it pulls an item from the iterator each time.

x = iter([1,2,3,4,5,6,7,8,9])
print zip(x, x, x)

回答 1

其他出色的答案和注释很好地解释了参数解压缩zip()的作用

就像Ignacioujukatzel所说的那样,您传递了zip()对同一迭代器的三个引用,并zip()从每个引用到迭代器按顺序生成了三元组的整数:

1,2,3,4,5,6,7,8,9  1,2,3,4,5,6,7,8,9  1,2,3,4,5,6,7,8,9
^                    ^                    ^            
      ^                    ^                    ^
            ^                    ^                    ^

并且由于您要求更详细的代码示例:

chunk_size = 3
L = [1,2,3,4,5,6,7,8,9]

# iterate over L in steps of 3
for start in range(0,len(L),chunk_size): # xrange() in 2.x; range() in 3.x
    end = start + chunk_size
    print L[start:end] # three-item chunks

下面的值startend

[0:3) #[1,2,3]
[3:6) #[4,5,6]
[6:9) #[7,8,9]

FWIW,map()初始参数为,您可以获得相同的结果None

>>> map(None,*[iter(s)]*3)
[(1, 2, 3), (4, 5, 6), (7, 8, 9)]

有关更多信息zip(),请访问map()http : //muffinresearch.co.uk/archives/2007/10/16/python-transpose-lists-with-map-and-zip/

The other great answers and comments explain well the roles of argument unpacking and zip().

As Ignacio and ujukatzel say, you pass to zip() three references to the same iterator and zip() makes 3-tuples of the integers—in order—from each reference to the iterator:

1,2,3,4,5,6,7,8,9  1,2,3,4,5,6,7,8,9  1,2,3,4,5,6,7,8,9
^                    ^                    ^            
      ^                    ^                    ^
            ^                    ^                    ^

And since you ask for a more verbose code sample:

chunk_size = 3
L = [1,2,3,4,5,6,7,8,9]

# iterate over L in steps of 3
for start in range(0,len(L),chunk_size): # xrange() in 2.x; range() in 3.x
    end = start + chunk_size
    print L[start:end] # three-item chunks

Following the values of start and end:

[0:3) #[1,2,3]
[3:6) #[4,5,6]
[6:9) #[7,8,9]

FWIW, you can get the same result with map() with an initial argument of None:

>>> map(None,*[iter(s)]*3)
[(1, 2, 3), (4, 5, 6), (7, 8, 9)]

For more on zip() and map(): http://muffinresearch.co.uk/archives/2007/10/16/python-transposing-lists-with-map-and-zip/


回答 2

我认为所有答案中都漏掉了一件事(对熟悉迭代器的人来说可能很明显),而对其他人却不太明显:

由于我们具有相同的迭代器,因此它会被消耗,而其余元素将由zip使用。因此,如果我们仅使用列表而不是迭代器。

l = range(9)
zip(*([l]*3)) # note: not an iter here, the lists are not emptied as we iterate 
# output 
[(0, 0, 0), (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, 5), (6, 6, 6), (7, 7, 7), (8, 8, 8)]

使用迭代器,弹出值并仅保持剩余可用,因此对于zip,一旦消耗了0,则1可用,然后2,依此类推。一件非常微妙的事情,但是非常聪明!!!

I think one thing that’s missed in all the answers (probably obvious to those familiar with iterators) but not so obvious to others is –

Since we have the same iterator, it gets consumed and the remaining elements are used by the zip. So if we simply used the list and not the iter eg.

l = range(9)
zip(*([l]*3)) # note: not an iter here, the lists are not emptied as we iterate 
# output 
[(0, 0, 0), (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, 5), (6, 6, 6), (7, 7, 7), (8, 8, 8)]

Using iterator, pops the values and only keeps remaining available, so for zip once 0 is consumed 1 is available and then 2 and so on. A very subtle thing, but quite clever!!!


回答 3

iter(s) 返回s的迭代器。

[iter(s)]*n 使s的n次相同迭代器的列表。

因此,在执行时zip(*[iter(s)]*n),它将从列表中的所有三个迭代器中依次提取一个项目。由于所有迭代器都是同一个对象,因此只将列表分组为n

iter(s) returns an iterator for s.

[iter(s)]*n makes a list of n times the same iterator for s.

So, when doing zip(*[iter(s)]*n), it extracts an item from all the three iterators from the list in order. Since all the iterators are the same object, it just groups the list in chunks of n.


回答 4

关于以这种方式使用zip的一个建议。如果长度不能被整除,它将截断您的列表。要解决此问题,如果可以接受填充值,则可以使用itertools.izip_longest。或者,您可以使用如下所示的内容:

def n_split(iterable, n):
    num_extra = len(iterable) % n
    zipped = zip(*[iter(iterable)] * n)
    return zipped if not num_extra else zipped + [iterable[-num_extra:], ]

用法:

for ints in n_split(range(1,12), 3):
    print ', '.join([str(i) for i in ints])

印刷品:

1, 2, 3
4, 5, 6
7, 8, 9
10, 11

One word of advice for using zip this way. It will truncate your list if it’s length is not evenly divisible. To work around this you could either use itertools.izip_longest if you can accept fill values. Or you could use something like this:

def n_split(iterable, n):
    num_extra = len(iterable) % n
    zipped = zip(*[iter(iterable)] * n)
    return zipped if not num_extra else zipped + [iterable[-num_extra:], ]

Usage:

for ints in n_split(range(1,12), 3):
    print ', '.join([str(i) for i in ints])

Prints:

1, 2, 3
4, 5, 6
7, 8, 9
10, 11

回答 5

正是看到什么是在Python解释器发生或可能更容易ipython使用n = 2

In [35]: [iter("ABCDEFGH")]*2
Out[35]: [<iterator at 0x6be4128>, <iterator at 0x6be4128>]

因此,我们有两个指向相同迭代器对象的迭代器的列表。请记住,iter在一个对象上返回一个迭代器对象,在这种情况下,由于使用*2python语法糖,它是同一迭代器两次。迭代器也只能运行一次。

此外,zip采用任意数量的可迭代数(序列iterables),并从每个输入序列的第i个元素创建元组。由于在我们的例子中,两个迭代器是相同的,因此zip对于输出的每个2元素元组两次将相同的迭代器移动两次。

In [41]: help(zip)
Help on built-in function zip in module __builtin__:

zip(...)
    zip(seq1 [, seq2 [...]]) -> [(seq1[0], seq2[0] ...), (...)]

    Return a list of tuples, where each tuple contains the i-th element
    from each of the argument sequences.  The returned list is truncated
    in length to the length of the shortest argument sequence.

解包(*)操作者确保迭代器运行至耗尽在这种情况下是,直到没有足够的输入以创建一个2元素的元组。

可以将其扩展为的任何值,nzip(*[iter(s)]*n)按照说明进行操作。

It is probably easier to see what is happening in python interpreter or ipython with n = 2:

In [35]: [iter("ABCDEFGH")]*2
Out[35]: [<iterator at 0x6be4128>, <iterator at 0x6be4128>]

So, we have a list of two iterators which are pointing to the same iterator object. Remember that iter on a object returns an iterator object and in this scenario, it is the same iterator twice due to the *2 python syntactic sugar. Iterators also run only once.

Further, zip takes any number of iterables (sequences are iterables) and creates tuple from i’th element of each of the input sequences. Since both iterators are identical in our case, zip moves the same iterator twice for each 2-element tuple of output.

In [41]: help(zip)
Help on built-in function zip in module __builtin__:

zip(...)
    zip(seq1 [, seq2 [...]]) -> [(seq1[0], seq2[0] ...), (...)]

    Return a list of tuples, where each tuple contains the i-th element
    from each of the argument sequences.  The returned list is truncated
    in length to the length of the shortest argument sequence.

The unpacking (*) operator ensures that the iterators run to exhaustion which in this case is until there is not enough input to create a 2-element tuple.

This can be extended to any value of n and zip(*[iter(s)]*n) works as described.


声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。