迭代器作为布尔语句?

时间:2017-08-19 13:49:31

标签: python python-2.7 python-3.x

我遇到了这段代码:

def myzip(*args):
    iters = map(iter, args)
    while iters:
        res = [next(i) for i in iters]
        yield tuple(res)

我不确定:

  • 为什么列表理解不需要捕获StopIteration
  • while iters如何运作,我已尝试过:

    x=[1,2]
    x=iter(x)
    if x: 
        print("Still True")
    next(x)
    next(x)
    if x: 
        print("Still True")
    

并且在两种情况下仍会打印"Still True"

该代码的作者也说过,因为map返回了一次性的可迭代的"在3.X和"一旦我们在循环中运行列表理解一次,它们就会耗尽但仍然是真的(并且res将永远是[])"。如果我们使用3.X,他建议使用list(map(iters, args)

我不确定此更改实际上如何帮助它工作,因为我认为即使迭代器位于StopIteration点,它仍然是True(基于我之前尝试过的) 。

编辑:

作者以此为例

>>> list(myzip('abc', 'lmnop'))
[('a', 'l'), ('b', 'm'), ('c', 'n')]

2 个答案:

答案 0 :(得分:7)

问题有几个方面。

蟒-2.x的

map返回list,而while iters只是确保代码没有进入循环,以防没有*args传入功能。这是因为空列表被视为False,非空列表被视为True

如果没有*args,它就不会进入循环并隐式return s然后会引发StopIteration

如果至少有一个参数,while iters等同于while True,它依赖于其中一个迭代器,在用尽之后引发StopIteration。 StopIteration并不需要被捕获(至少在Python 3.7之前),因为你希望myzip在一个可迭代用尽时停止

蟒-3.X

在Python 3中,map会返回map个实例,该实例始终会被视为True,因此while循环等同于while True

但是,python-3.x中存在一个问题:迭代map实例后,它将耗尽。在第一次迭代(while循环)中按预期工作,但在下一次迭代中map将为空,它将只创建一个空列表:

>>> it = map(iter, ([1,2,3], [3,4,5]))
>>> [next(sub) for sub in it]
[1, 3]
>>> [next(sub) for sub in it]
[]

没有任何东西可以引发StopIteration,因此它将进入无限循环并永远返回空tuple。如果while - 列表为空,那也是您不想进入iters循环的原因!

可以使用以下方法修复(如上所述)

iters = list(map(iter, args))

一般观察

只是说明如何更有意义:

def myzip(*args):
    if not args:
        return
    iters = [iter(arg) for arg in args]  # or list(map(iter, args))
    while True:
        res = [next(i) for i in iters]
        yield tuple(res)

如果您希望代码符合python-3.7(感谢@Kevin指出这一点),您明确需要捕获StopIteration。有关更多信息,请参阅PEP-479

def myzip(*args):
    if not args:
        return
    iters = [iter(arg) for arg in args]  # or list(map(iter, args))
    while True:
        try:
            res = [next(i) for i in iters]
        except StopIteration:  
            # the StopIteration raised by next has to be catched in python-3.7+
            return
        yield tuple(res)

后一个代码也适用于python-2.7和python-3.x< 3.7但它只需要捕获python 3.7 +中的StopIteration

答案 1 :(得分:4)

  

为什么列表理解不需要捕获StopIteration

此循环的整个用于next()引发StopIteration并且列表理解不会捕获它。这就是这个生成器退出的方式; while循环基本上是无穷无尽的,它测试的条件是稳定的。

这样,当其中一个输入耗尽时,发电机会整齐地停止:

>>> g = myzip([1], [2, 3])
>>> next(g)
(1, 2)
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in myzip
StopIteration

StopIteration的第一个迭代器上next()调用引发了iters异常。

从本质上讲,如果您在没有输入的情况下调用while iters:,那么False所做的就是返回myzip()

>>> list(myzip())
[]

否则,它也可以使用while True:来表示所有差异。

现在,在Python 3中,map(iter, args)返回一个迭代器,而不是一个列表。该对象只能迭代一次,但不被视为空并且总是具有 true 的布尔值。这意味着第二次浏览while iters:循环iters为真,但[next(i) for i in iters]迭代0次,next()永远不会被调用,所需StopIteration从不提出退出发电机。这就是为什么作者建议list(map(iter, args))作为解决办法,将map迭代器值捕获到列表中。