迭代器作为布尔语句?

Dan*_*Dan 6 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)
Run Code Online (Sandbox Code Playgroud)

我不确定:

  • 为什么列表理解不需要捕获StopIteration
  • while iters我怎么努力工作:

    x=[1,2]
    x=iter(x)
    if x: 
        print("Still True")
    next(x)
    next(x)
    if x: 
        print("Still True")
    
    Run Code Online (Sandbox Code Playgroud)

它仍然"Still True"在两种情况下打印.

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

我不确定这种变化实际上如何帮助它工作,因为我认为即使迭代器StopIteration仍然存在True(基于我之前尝试过的).

编辑:

作者以此为例

>>> list(myzip('abc', 'lmnop'))
[('a', 'l'), ('b', 'm'), ('c', 'n')]
Run Code Online (Sandbox Code Playgroud)

MSe*_*ert 7

这个问题有几个方面.

蟒蛇-2.X

map回报listwhile iters公正,使确保代码不会进入的情况下,没有循环*args传递给函数.那是因为考虑了空列表并考虑False了非空列表True.

如果没有*args它将不会进入循环并隐式地进入循环return,然后提出一个StopIteration.

如果至少有一个参数while iters相当于while True并且它依赖于其中一个迭代器来StopIteration在用完之后引发a .不需要捕获StopIteration(至少在Python 3.7之前),因为如果一个iterable耗尽,你想要myzip停止.

蟒蛇-3.X

在Python 3中,map返回一个map总是被认为Truewhile循环等效的实例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]
[]
Run Code Online (Sandbox Code Playgroud)

没有任何东西可以提升StopIteration,所以它将进入无限循环并tuple永远返回空.while如果iters-list为空,那也是你不想进入循环的原因!

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

iters = list(map(iter, args))
Run Code Online (Sandbox Code Playgroud)

一般观察

只是说明如何更有意义:

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)
Run Code Online (Sandbox Code Playgroud)

如果你希望代码符合python-3.7(感谢@Kevin指出这一点)你明确需要捕获StopIterations.有关更多信息,请参阅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)
Run Code Online (Sandbox Code Playgroud)

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

  • 而不是做`if not iters:return None`为什么不做`if not args:在函数的开头返回None`?这样,如果没有参数,你甚至不必尝试计算`iters`. (3认同)
  • [在生成器函数中,不允许return语句包含expression_list.在该上下文中,裸返回表示生成器已完成并将导致StopIteration被提升.](https://docs.python.org/2.7/reference/simple_stmts.html#the-return-statement)其次,如果测试不存在你在无限循环中有`res = []`.这会给执行`zip(*emptylist)`的代码带来麻烦. (2认同)
  • 在Python 3.3及更高版本中似乎就是这种情况(设置StopIteration的value属性).在2.7中,返回表达式是被禁止的.我们都学到了东西:) (2认同)
  • 从Python 3.7开始,[你的代码将无法工作,因为`StopIteration`被转换为`RuntimeError`](https://www.python.org/dev/peps/pep-0479/). (2认同)