了解Python3.x中的izip

qui*_*bug 2 python iterator python-2.x python-3.x

我的问题只是学习目的,而且只在python3.x上.在现实生活中,我将使用zip,因为python3 zip与python2 izip完成相同的工作(即返回生成器,而不是真实的东西).

在python2中,izip基本上等同于下面的代码(从izip中挑选,加上一些调试代码)

def izip(*iterables):
    iterators = map(iter, iterables)
    n = 0
    while iterators:
        x = tuple(map(next, iterators))
        print("at n={}, x={} ".format(n, x))
        yield x
        n += 1
        if n > 10: break
Run Code Online (Sandbox Code Playgroud)

Python2工作正常.输出izip('abc', 'ABC')是:

at n=0, x=('a', 'A') 
('a', 'A')
at n=1, x=('b', 'B') 
('b', 'B')
at n=2, x=('c', 'C') 
('c', 'C')
Run Code Online (Sandbox Code Playgroud)

Python3进入无限循环.原因在这个帖子中解释了.但还有另一点我无法理解:python3只产生第一个元组.这是同一程序的输出.为什么bs'和cs'没有出现?:

at n=0, x=('a', 'A') 
('a', 'A')
at n=1, x=() 
()
at n=2, x=() 
()
at n=3, x=() 
() etc.
Run Code Online (Sandbox Code Playgroud)

我的两个问题是为什么Python3会这样做?以及如何使此代码工作?

shx*_*hx2 7

问题在于mappython2和python3之间的不同行为,特别是在第一次调用map(iterators = map(iter, iterables))时.

在python3中,map返回一个生成器(或类似于生成器的对象),而在python2中,它是一个列表.这意味着在第一次调用之后tuple(map(next, iterators)),iterators生成器被完全占用,因此在下一次迭代中,没有更多的迭代器可以使用.

如果你改变它应该工作:

iterators = map(iter, iterables) 
Run Code Online (Sandbox Code Playgroud)

至:

iterators = list(map(iter, iterables)) 
Run Code Online (Sandbox Code Playgroud)

(这可以说是更好的iterators = [ iter(it) for it in iterables ])


正如你所指出的,它现在进入无限循环.同样,问题在于map功能,但这次是在第二次调用中.

首先,让我们了解这个实现在python2中是如何工作的.尽管有一个while iterators循环,但由于条件为false,循环不会中断,但是由于其中一个调用引发了StopIteration异常next.此异常传播到调用者的循环,正确理解没有更多结果.

实现它可能是直观的:

def izip(*iterables):
    if not iterables: return []
    iterators = map(iter, iterables)
    while True:
        yield tuple(map(next, iterators))
Run Code Online (Sandbox Code Playgroud)

现在,mappython3中的行为也发生了变化.而不是提高,它"修剪"输出:

list(map(next, [ iter('ab'), iter('') ]))
=> ['a']
Run Code Online (Sandbox Code Playgroud)

不提高StopIteration会导致无限循环.

解决方案是使用列表推导,它传播StopIteration异常.

def izip(*iterables):
    if not iterables: return []
    iterators = map(iter, iterables)
    while True:
        yield tuple([ next(it) for it in iterators ])
Run Code Online (Sandbox Code Playgroud)

教训:列表解析(和发电机表达式)应该受到青睐map,filter等等.