为什么Contextmanager抛出运行时错误'生成器在throw()'之后没有停止?

时间:2016-01-13 19:11:18

标签: python contextmanager

在我的utility.py中,

git

在我的task.py中,我打电话:

@contextmanager
def rate_limit_protection(max_tries=3, wait=300):
    tries = 0
    while max_tries > tries:
        try:
            yield
            break
        except FacebookRequestError as e:
            pprint.pprint(e)
            if e._body['error']['message'] == '(#17) User request limit reached':
                print("waiting...")
                time.sleep(wait)
                tries += 1

在给定日期范围运行任务后,一旦Facebook速率限制启动,程序将等待300秒,然后失败并显示错误。

for date in interval:
   with utility.rate_limit_protection():
      stats = account.get_insights(params=params)

1 个答案:

答案 0 :(得分:19)

with语句不是循环结构。它不能用于重复执行代码。使用@contextmanager创建的上下文管理器只能yield一次。

上下文管理器(基本上)做了三件事:

  1. 它在代码块之前运行一些代码。
  2. 在代码块之后运行一些代码。
  3. 可选地,它可以抑制代码块中引发的异常。
  4. 如果你想做这样的事情,你需要重写它,以便将循环移到上下文管理器之外,或者根本没有上下文管理器。

    一种选择是编写一个接受回调作为参数的函数,然后在循环中调用回调,就像你在上下文管理器中那样:

    def do_rate_protection(callback, max_tries=3):
        tries = 0
        while max_tries > tries:
            try:
                callback()
                break
            except FacebookRequestError as e:
                # etc.
    

    然后你可以这样称呼它:

    for date in interval:
        def callback():
            # code
        do_rate_protection(callback)
    

    如果回调不需要date变量,则可以将其移出循环以避免重复创建相同的函数(这会浪费资源)。您还可以date callback() {{1}}函数的参数,并使用functools.partial传递它。