【问题标题】:Elegant way for breaking a Generator Loop: GeneratorExit error打破生成器循环的优雅方法:GeneratorExit 错误
【发布时间】:2018-03-14 12:09:18
【问题描述】:

我有一个在生成器上循环的代码。如果它到达它,我必须在第二次迭代后打破该循环。为此,我使用break,它在调用Generator.close() 方法时引发GeneratorExit

for page in limit_handled(tweepy.Cursor(..., ..., ...):
    while len(the_list) < 400:
        for status in page:

            def process_status(tweet):
                ...
                ...

            the_list.append(process_status(status))

    break

是否有更优雅的方式来避免此类错误?

Exception ignored in: <generator object limit_handled at 0x000000003AB300A0>
RuntimeError: generator ignored GeneratorExit

我已经看到了这两个问题的答案:How to take the first N...How to get the n next...,但这不是同一个问题。就我而言,Generator 使用Cursor。因此,在每次迭代中,它都会处理一个查询。我想在至少达到 400 个状态后停止查询,这可能发生在第二次或第三次迭代之后(查询通常返回 200 行,但也可以更少)。切片生成器在这里不是一个选项。避免处理所有查询(总共 16 个,大约 16*200=3200 个状态)正是我想通过在返回 400 个状态后破坏代码来避免的。

编辑:为了更好地理解,这里是我的生成器的代码:

def limit_handled(cursor):
    global user_timeline_remaining
    while True:
        if user_timeline_remaining>1:
            try:
                yield cursor.next()
            except BaseException as e:
                print('failed_on_CURSOR_NEXT', str(e))
        else:
            time.sleep(5*60)
                try:
                    data = api.rate_limit_status()
                except BaseException as f:
                    print('failed_on_LIMIT_STATUS', str(f))
                user_timeline_remaining = data['remaining_queries']

【问题讨论】:

  • 那为什么不在生成器中处理异常呢? try: ... except GeneratorExit: pass.
  • 并且close() 不是(直接)被for 循环调用,它在没有更多对生成器对象的引用时被调用(假设for 循环是唯一的引用,当循环结束时,引用被删除并且生成器被删除)。
  • 对不起,我认为很明显它是在调用 break 时引发的(暗示还有生成器迭代)
  • 是的,break 结束了for 循环。 for 循环结束将引用计数递减为limit_handled(...) 的结果。因为那是唯一的引用,所以调用生成器__del__ 方法来清理它,它调用generator.close()
  • 明白了。无论如何,我不确定你的第一个答案是什么意思(使用try:... except GeneratorExit : pass ),你能解释一下吗?

标签: python python-3.x generator break


【解决方案1】:

您的生成器忽略GeneratorExit 引发的generator.close() 异常。通过捕获BaseException,您实际上使关闭变得不可能,因此您的生成器将改为产生另一个值(因为代码在异常处理程序返回循环顶部后继续,直到再次到达yield )。这就是您看到异常的原因:

如果生成器产生一个值,则会引发 RuntimeError

你真的不应该在你的代码中捕获BaseException。捕获特定异常,或者,最好是Exception

except Exception a e:
    # ...

这样你就不会捕捉到GeneratorExitBaseException 的子类)、SystemExitKeyboardInterrupt

如果您觉得此时您的代码必须捕获 SystemExitKeyboardInterrupt,请至少在您的 except BaseException as e: 处理程序之前添加一个 except GeneratorExit: return

【讨论】:

    猜你喜欢
    • 2020-05-28
    • 1970-01-01
    • 2016-05-16
    • 2016-10-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-28
    相关资源
    最近更新 更多