【问题标题】:StopIteration in generators生成器中的停止迭代
【发布时间】:2017-10-06 14:15:42
【问题描述】:

我正在学习 python 的生成器、迭代器、可迭代对象,但我无法解释为什么以下内容不起作用。作为练习,我想创建一个简单版本的函数 zip。这是我所做的:

def myzip(*collections):

    iterables = tuple(iter(collection) for collection in collections)

    yield tuple(next(iterable) for iterable in iterables)

test = myzip([1,2,3],(4,5,6),{7,8,9})

print(next(test))
print(next(test))
print(next(test))

我做的是:

  • 我有 collections 这是一些集合的元组
  • 我创建了一个新元组iterables,对于每个集合(可迭代),我使用iter 获取迭代器
  • 然后,我创建一个新元组,在每个可迭代对象上,我调用next。然后这个元组就是 yield。

所以我希望在第一次执行时创建(并存储)对象iterables。然后在每次迭代(包括第一次)中,我对之前存储的每个迭代调用 next 并返回它。

但这是我得到的:

(1, 4, 8)
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-108-424963a58e58> in <module>()
      8 
      9 print(next(test))
---> 10 print(next(test))

StopIteration: 

所以我看到第一次迭代很好,结果是正确的。但是,第二次迭代引发了 StopIteration 异常,我不明白为什么:每个迭代仍然有一些值,所以 nexts 没有一个返回 StopIteration。事实上,这是可行的:

def myziptest(*collections):

    iterables = tuple(iter(collection) for collection in collections)

    for _ in range(3):
        print(tuple(next(iterable) for iterable in iterables))

test = myziptest([1,2,3],(4,5,6),{7,8,9})

输出:

(1, 4, 8)
(2, 5, 9)
(3, 6, 7)

那么发生了什么? 非常感谢

【问题讨论】:

    标签: python-3.x iterator generator


    【解决方案1】:

    这是一个可行的解决方案

    def myzip(*collections):
    
        iterables = tuple(iter(collection) for collection in collections)
    
        while True:
            try:
                yield tuple([next(iterable) for iterable in iterables])
            except StopIteration:
                # one of the iterables has no more left.                
                break
    
    test = myzip([1,2,3],(4,5,6),{7,8,9})
    
    print(next(test))
    print(next(test))
    print(next(test))
    

    这段代码和你的不同之处在于你的代码只产生一个结果。意思是,多次调用 next 会给你一个 StopIteration。

    yield x 视为将x 放入队列,将next 视为从该队列中弹出。当您尝试从空队列中弹出时,您会得到Stopiteration。放多少就可以弹出多少。

    【讨论】:

    • 好吧,这真是一个愚蠢的错误......我忘了添加一个循环,所以很明显这个函数在第一个 next() 之后就死了。谢谢!
    • 没问题。仅供参考,即使集合的大小不同,tuple(next(iterable) for iterable in iterables) 也将继续工作。将其设为tuple([next(iterable) for iterable in iterables]) 将触发 StopIteration,因此您可以在最短的公共长度处停止。
    • 事实上,我看到如果我这样做for i in test: print(i),我会得到一个无限循环。有什么区别?
    • 不同的是,tuple(i for i in foo)tuple(generator),生成器可以返回一些空的东西。 tuple([i for i in foo])tuple(list),它强制生成器展开。当它展开时(请原谅我的条款),next(...) 触发StopIteration。这是一个例子 - link
    • 我已经更新了代码,所以没有人对无限循环感到惊讶。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-16
    • 2016-01-20
    • 2015-12-15
    • 2023-02-06
    • 1970-01-01
    相关资源
    最近更新 更多