【问题标题】:How does zip(*[iter(s)]*n) work in Python?zip(*[iter(s)]*n) 如何在 Python 中工作?
【发布时间】:2011-01-15 01:09:08
【问题描述】:
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) 是如何工作的?如果用更冗长的代码编写会是什么样子?

【问题讨论】:

标签: python iterator


【解决方案1】:

iter() 是一个序列的迭代器。 [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)

【讨论】:

  • 温馨提示:当迭代器yields (= returns) 一个项目时,您可以将这个项目想象为“已消费”。所以下次调用迭代器时,它会产生下一个“未使用”的项目。
【解决方案2】:

其他很好的答案和 cmets 很好地解释了 argument unpackingzip() 的作用。

正如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-transposing-lists-with-map-and-zip/

【讨论】:

    【解决方案3】:

    我认为所有答案中遗漏的一件事(对于熟悉迭代器的人来说可能很明显)但对其他人来说并不那么明显 -

    因为我们有相同的迭代器,它被消耗掉,剩下的元素被 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 以此类推。很微妙的事情,但很聪明!!!

    【讨论】:

    • +1,你救了我!假设每个人都知道这一点,我无法相信其他答案跳过了这个重要的细节。您能否参考包含此信息的文档?
    【解决方案4】:

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

    [iter(s)]*n 为 s 创建一个包含 n 次相同迭代器的列表。

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

    【讨论】:

    • 不是“同一个列表的n个迭代器”,而是“同一个迭代器对象的n倍”。不同的迭代器对象不共享状态,即使它们属于同一个列表。
    • 谢谢,已更正。确实这就是我“想”的,但写了别的东西。
    【解决方案5】:

    关于以这种方式使用 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
    

    【讨论】:

    【解决方案6】:

    使用n = 2 可能更容易看到python 解释器或ipython 中发生的事情:

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

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

    此外,zip 接受任意数量的迭代(sequencesiterables)并从每个输入序列的第 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.
    

    unpacking (*) operator 确保迭代器耗尽,在这种情况下,直到没有足够的输入来创建一个 2 元素元组。

    这可以扩展到n 的任何值,zip(*[iter(s)]*n) 的工作方式与描述相同。

    【讨论】:

    • 抱歉有点慢。但是你能解释一下“由于 *2 python 语法糖,同一个迭代器两次。迭代器也只运行一次。”请部分?如果是这样,为什么结果不是 [("A", "A")....]?谢谢。
    • @BowenLiu * 只是为了方便复制对象。尝试使用标量,然后使用列表。也可以试试print(*zip(*[iter("ABCDEFG")]*2))print(*zip(*[iter("ABCDEFG"), iter("ABCDEFG")]))。然后开始将两者分解成更小的步骤,看看这两个语句中的实际迭代器对象是什么。
    【解决方案7】:

    我需要分解每个部分步骤以真正内化它的工作原理。我的 REPL 笔记:

    >>> # refresher on using list multiples to repeat item
    >>> lst = list(range(15))
    >>> lst
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
    >>> # lst id value
    >>> id(lst)
    139755081359872
    >>> [id(x) for x in [lst]*3]
    [139755081359872, 139755081359872, 139755081359872]
    
    # replacing lst with an iterator of lst
    # It's the same iterator three times
    >>> [id(x) for x in [iter(lst)]*3 ]
    [139755085005296, 139755085005296, 139755085005296]
    # without starred expression zip would only see single n-item list.
    >>> print([iter(lst)]*3)
    [<list_iterator object at 0x7f1b440837c0>, <list_iterator object at 0x7f1b440837c0>, <list_iterator object at 0x7f1b440837c0>]
    # Must use starred expression to expand n arguments
    >>> print(*[iter(lst)]*3)
    <list_iterator object at 0x7f1b4418b1f0> <list_iterator object at 0x7f1b4418b1f0> <list_iterator object at 0x7f1b4418b1f0>
    
    # by repeating the same iterator, n-times,
    # each pass of zip will call the same iterator.__next__() n times
    # this is equivalent to manually calling __next__() until complete
    >>> iter_lst = iter(lst)
    >>> ((iter_lst.__next__(), iter_lst.__next__(), iter_lst.__next__()))
    (0, 1, 2)
    >>> ((iter_lst.__next__(), iter_lst.__next__(), iter_lst.__next__()))
    (3, 4, 5)
    >>> ((iter_lst.__next__(), iter_lst.__next__(), iter_lst.__next__()))
    (6, 7, 8)
    >>> ((iter_lst.__next__(), iter_lst.__next__(), iter_lst.__next__()))
    (9, 10, 11)
    >>> ((iter_lst.__next__(), iter_lst.__next__(), iter_lst.__next__()))
    (12, 13, 14)
    >>> ((iter_lst.__next__(), iter_lst.__next__(), iter_lst.__next__()))
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    StopIteration
    
    # all together now!
    # continuing with same iterator multiple times in list
    >>> print(*[iter(lst)]*3)
    <list_iterator object at 0x7f1b4418b1f0> <list_iterator object at 0x7f1b4418b1f0> <list_iterator object at 0x7f1b4418b1f0>
    >>> zip(*[iter(lst)]*3)
    <zip object at 0x7f1b43f14e00>
    >>> list(zip(*[iter(lst)]*3))
    [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14)]
    
    # NOTE: must use list multiples. Explicit listing creates 3 unique iterators
    >>> [iter(lst)]*3 == [iter(lst), iter(lst), iter(lst)]
    False
    >>> list(zip(*[[iter(lst), iter(lst), iter(lst)]))
    [(0, 0, 0), (1, 1, 1), (2, 2, 2), (3, 3, 3), ....    
    

    【讨论】:

      猜你喜欢
      • 2011-01-15
      • 2016-02-16
      • 1970-01-01
      • 2012-03-20
      • 2022-11-17
      • 2019-04-29
      • 2021-03-04
      • 1970-01-01
      相关资源
      最近更新 更多