在Python中重新运行代码块

时间:2016-09-02 05:18:00

标签: python python-2.7 generator yield with-statement

我想找出一种方法来写这样的东西:

code_that_should_not_be_run_again()
with rerun_code_that_may_fail():
  another_method(x)
  run_me_again_if_i_fail(y)
code_that_should_only_be_run_if_above_succeeds()

上面会运行该代码,并捕获异常,如果它被捕获,那么再次尝试运行代码。这将是我想要的更长版本:

code_that_should_not_be_run_again()
try:
  another_method(x)
  run_me_again_if_i_fail(y)
catch Exception:
  try:
    another_method(x)
    run_me_again_if_i_fail(y)
  catch Exception:
    raise Exception("Couldn't run")
 code_that_should_only_be_run_if_above_succeeds()

我以为我可能会使用一个生成器,也许是抓住了将内容输入到lambda然后以某种方式运行它两次,但现在确定我可以编写这样的代码。

这在Python中可行吗?或者也许可以做类似的事情?

这是我迄今为止所做的尝试:

from contextlib import contextmanager
@contextmanager
def run_code():
    print 'will run'
    try:
      yield
    except SomeException:
      try:
        yield
      except SomeException:
        raise SomeException('couldn't run')

编辑 Python不会'让你做我想做的事,所以你只能在函数上使用装饰器:(

2 个答案:

答案 0 :(得分:2)

使用重试装饰器 - https://pypi.python.org/pypi/retry/ - 并且在您想要捕获任何TypeError的场景下,最多尝试3次,延迟为5秒:

from retry import retry

@retry(TypeError, tries=3, delay=5)
def code_block_that_may_fail():
    method_1()
    method_2()
    #Some more methods here...

code_block_that_may_fail()

不能比那更清洁。

此外,您可以使用内置记录器记录失败的尝试(请参阅文档)。

答案 1 :(得分:0)

我希望有人发布一个比这更好的解决方案,但我会使用这个方法,也许还有一个装饰器:

def retry_if_fails(fn, exception=Exception, *args, **kwargs):
    try:
        fn(*args, **kwargs)
    except exception:
        fn(*args, **kwargs)  # if this fails again here, the exception bubbles up

当然,问题是您只在retry_if_fails中调用一个函数,而不是像您所做的那样调用两步函数。

您可以创建一个函数列表,并将其与一个单独的列表列表一起传递给您想要处理的每个函数的参数。

def retry_funcs(fns, all_args, all_kwargs, exception=Exception):
    # all_args is a list of lists
    # all_kwargs is a list of dicts
    try:
        for fn, args, kwargs in zip(fns, all_args, all_kwargs):
            fn(*args, **kwargs)
    except exception:
        for fn, args, kwargs in zip(fns, all_args, all_kwargs):
            fn(*args, **kwargs)

在这一个中,args的列表列表和kwargs的词典列表必须按顺序匹配。 all_argsall_kwargs中的空列表或空字典将使您不会将任何args传递给特定函数,或仅传递args,或仅传递kwargs,或两者都传递。

fns = [some_func, another_func]
all_args = [['a', 'b'],  # args for some_func
            []           # no args for another_func
           ]
all_kwargs = [{'param': 'something'}, # kwargs for some_func
              {}                      # no kwargs
             ]

而不是funcsargskwargs的列表不同,可能更容易将它们放在一起,就像zip的结果一样 - 这将是,因为你知道如何知道电话中的代码:

fns_with_args_and_kwargs = [(some_func, ['a', 'b'], {'param': 'something'}),
                            (another_func, [], {})
                           ]
# and then
for fn, args, kwargs in fns_with_args_and_kwargs:
    fn(*args, **kwargs)