我的问题只是学习目的,而且只在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
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')
Python3进入无限循环。原因在this thread中解释。但还有另一点我无法理解:python3只产生第一个元组。这是同一程序的输出。为什么bs'和cs'没出现?:
at n=0, x=('a', 'A')
('a', 'A')
at n=1, x=()
()
at n=2, x=()
()
at n=3, x=()
() etc.
我的两个问题是为什么Python3会以这种方式运行?以及如何使这个代码工作?
答案 0 :(得分:6)
问题在于map
在python2和python3之间的不同行为,特别是在第一次调用map
(iterators = map(iter, iterables)
)时。
在python3中,map
返回一个生成器(或类似于生成器的对象),而在python2中,它是一个列表。这意味着在第一次调用tuple(map(next, iterators))
之后,iterators
生成器被完全消耗,因此在下一次迭代中,不再需要迭代器。
如果你改变了它应该有用:
iterators = map(iter, iterables)
为:
iterators = list(map(iter, iterables))
(可以说是iterators = [ iter(it) for it in iterables ]
更好)
正如您所指出的,它现在进入无限循环。同样,问题在于map
函数,但这次是在第二次调用中。
首先,让我们了解这个实现在python2中是如何工作的。尽管有一个while iterators
循环,但由于条件为false,循环不会中断,但由于其中一个调用next
引发了StopIteration异常。此异常传播到调用者的循环,正确理解没有更多结果。
实现它可能是直观的:
def izip(*iterables):
if not iterables: return []
iterators = map(iter, iterables)
while True:
yield tuple(map(next, iterators))
现在,map
的行为也在python3中发生了变化。而不是提高,它"修剪"输出:
list(map(next, [ iter('ab'), iter('') ]))
=> ['a']
不提高StopIteration导致无限循环。
解决方案是使用列表推导, 传播StopIteration异常。
def izip(*iterables):
if not iterables: return []
iterators = map(iter, iterables)
while True:
yield tuple([ next(it) for it in iterators ])
获得的经验:列表推导(和生成器表达式)should be favored超过map
,filter
等。