破坏生成器循环的优雅方式:GeneratorExit错误

时间:2017-10-03 10:10:16

标签: python python-3.x generator break

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

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']

1 个答案:

答案 0 :(得分:3)

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

  

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

您真的不应该在代码中捕获RuntimeError。抓住特定例外,或者最好是BaseException

Exception

这样你就不会抓住except Exception a e: # ... GeneratorExit的子类),BaseExceptionSystemExit

如果您觉得您的代码此时必须抓住KeyboardInterruptSystemExit,请至少在KeyboardInterrupt处理程序之前添加except GeneratorExit: return