将重试封装到`with`块中

Mic*_*all 28 python exception-handling with-statement contextmanager

我希望将数据库事务的逻辑封装到一个with块中; 将代码包装在事务中并处理各种异常(锁定问题).这很简单,但是我想让块在某些异常后封装代码块的重试.我无法看到一种方法将它整齐地打包到上下文管理器中.

是否可以在with语句中重复代码?

我想像它一样使用它,这真的很整洁.

def do_work():
    ...
    # This is ideal!
    with transaction(retries=3):
        # Atomic DB statements
        ...
    ...
Run Code Online (Sandbox Code Playgroud)

我目前正在使用装饰器处理它,但我更愿意提供上下文管理器(或实际上两者),所以我可以选择在with块中包含几行代码而不是包装在装饰器中的内联函数,这就是我现在所做的:

def do_work():
    ...
    # This is not ideal!
    @transaction(retries=3)
    def _perform_in_transaction():
        # Atomic DB statements
        ...
    _perform_in_transaction()
    ...
Run Code Online (Sandbox Code Playgroud)

小智 13

是否可以在with语句中重复代码?

没有.

正如之前在邮件列表线程中指出的那样,您可以通过使装饰器调用传递的函数来减少一些重复:

def do_work():
    ...
    # This is not ideal!
    @transaction(retries=3)
    def _perform_in_transaction():
        # Atomic DB statements
        ...
    # called implicitly
    ...
Run Code Online (Sandbox Code Playgroud)


Hen*_*ter 6

我这样做的方法只是实现一个标准的数据库事务上下文管理器,但允许它retries在构造函数中接受一个参数.然后我将在你的方法实现中将其包装起来.像这样的东西:

class transaction(object):
    def __init__(self, retries=0):
        self.retries = retries
    def __enter__(self):
        return self
    def __exit__(self, exc_type, exc_val, traceback):
        pass

    # Implementation...
    def execute(self, query):
        err = None
        for _ in range(self.retries):
            try:
                return self._cursor.execute(query)
            except Exception as e:
                err = e # probably ought to save all errors, but hey
        raise err

with transaction(retries=3) as cursor:
    cursor.execute('BLAH')
Run Code Online (Sandbox Code Playgroud)


JAB*_*JAB 4

由于装饰器本身就是函数,因此您可以执行以下操作:

with transaction(_perform_in_transaction, retries=3) as _perf:
    _perf()
Run Code Online (Sandbox Code Playgroud)

有关详细信息,您需要实现transaction()为工厂方法,该方法返回一个带有__callable__()set 的对象来调用原始方法并在失败时重复最多retries次数;__enter__()并将__exit__()被定义为数据库事务上下文管理器的正常情况。

您也可以进行设置transaction(),使其本身执行传递的方法最多retries次数,这可能需要与实现上下文管理器相同的工作量,但意味着实际使用量将减少到仅transaction(_perform_in_transaction, retries=3)(事实上, ,相当于 delnan 提供的装饰器示例)。