Python3的内置zip功能问题

A_U*_*ser 5 python zip

Python 3.4.2 (default, Oct  8 2014, 13:44:52) 
[GCC 4.9.1 20140903 (prerelease)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> gen = (x for x in range(10)) ## Need to wrap range into ()'s to create a generator, next(range(10)) is invalid
>>> list(zip(gen, [1,2,3])) ## zip will "eat up" the number 3
[(0, 1), (1, 2), (2, 3)]
>>> next(gen) ## Here i need next to return 3
4
>>> 
Run Code Online (Sandbox Code Playgroud)

问题是我在拉链电话后丢失了一个值.如果gen不是纯粹的代码,这将是一个更大的问题.

我不知道是否可以创建一个行为类似的函数,如果zip函数的一个参数只是一个生成器,其余的是"正常"迭代器,其中所有的值都是已知,并存储在内存中.如果是这种情况,您可以最后检查发电机.

基本上我想知道的是,如果python标准库中有任何函数,就像我在这种情况下需要的那样.

当然,在某些情况下,人们可以做类似的事情

xs = list(gen)
Run Code Online (Sandbox Code Playgroud)

那你只需要处理一个清单.

我还可以补充一点,从gen获得zip的最后一个值也可以解决这个问题.

Mar*_*ers 4

不,没有内置函数可以避免这种行为。

所发生的情况是,该zip()函数尝试获取所有输入的下一个值,以便它可以生成下一个元组。它必须按顺序执行此操作,并且该顺序与传入的参数相同才是合乎逻辑的。事实上,文档保证了该顺序

保证可迭代对象从左到右的求值顺序

由于该函数需要支持任意迭代,zip()因此不会尝试确定所有参数的长度。它不知道你的第二个参数只有 3 个元素。它只是尝试获取每个参数的下一个值,构建一个元组并返回该值。如果任何参数无法产生下一个值,则zip()迭代器完成。但这确实意味着它会在询问列表之前首先询问生成器下一个元素。

除了改变输入的顺序之外,您zip()还可以构建自己的函数,该函数尝试在可用的情况下考虑长度:

def limited_zip(*iterables):
    minlength = float('inf')
    for it in iterables:
        try:
            if len(it) < minlength:
                minlength = len(it)
        except TypeError:
            pass
    iterators = [iter(it) for it in iterables]
    count = 0
    while iterators and count < minlength:
        yield tuple(map(next, iterators))
        count += 1
Run Code Online (Sandbox Code Playgroud)

因此,该版本的zip()函数尝试获取您传入的任何序列的最小长度。这并不能保护您在混合中使用较短的可迭代对象,但确实适用于您的测试用例:

演示:

>>> gen = iter(range(10))
>>> list(limited_zip(gen, [1, 2, 3]))
[(0, 1), (1, 2), (2, 3)]
>>> next(gen)
3
Run Code Online (Sandbox Code Playgroud)