我想实现一种只使用上下文管理器重复一段代码所需次数的方法,因为它的语法非常好。如下所示:
with try_until_success(attempts=10):
command1()
command2()
command3()如果没有发生错误,则必须执行一次命令。如果发生错误,则应该再次执行它们,直到10次尝试通过为止,如果是,则必须引发错误。例如,重新连接到数据库可能很有用。我表示的语法是字面的,我不想修改它(所以不要建议我用一种for of while语句来替换它)。
有什么方法可以在try_until_success中实现来做我想做的事情吗?
我试过的是:
from contextlib import contextmanager
@contextmanager
def try_until_success(attempts=None):
counter = 0
while True:
try:
yield
except Exception as exc:
pass
else:
break
counter += 1
if attempts is not None and counter >= attempts:
raise exc这给了我一个错误:
RuntimeError: generator didn't stop after throw()我知道,有很多方法可以使用循环而不是with-statement或者在装饰师的帮助下达到我所需要的。但两者都有语法上的缺点。例如,在循环的情况下,我必须插入try-except块,对于装饰器,我必须定义一个新函数。
我已看过以下问题:
他们对我的问题没有帮助。
发布于 2020-11-10 14:14:22
问题是with语句的主体没有在对try_until_success的调用中运行。该函数使用__enter__方法返回一个对象;__enter__方法调用并返回该对象,然后执行with语句的主体。没有关于将主体封装在任何类型的循环中的规定,这样就可以在到达with语句的结尾时重复它。
发布于 2020-11-10 14:06:33
这有悖于上下文管理器设计的工作方式,您可能不得不使用非标准的技巧,比如修补字节码。
有关如何展开这些文档,请参见带语句上的正式文档和原始佩普343。它可能会帮助你理解为什么这不会得到官方的支持,也可能帮助你理解为什么其他的评论者通常会说这是一件坏事。
作为可能奏效的例子,不妨尝试:
class try_until_success:
def __init__(self, attempts):
self.attempts = attempts
self.attempt = 0
self.done = False
self.failures = []
def __iter__(self):
while not self.done and self.attempt < self.attempts:
i = self.attempt
yield self
assert i != self.attempt, "attempt not attempted"
if self.done:
return
if self.failures:
raise Exception("failures occurred", self.failures)
def __enter__(self):
self.attempt += 1
def __exit__(self, _ext, exc, _tb):
if exc:
self.failures.append(exc)
return True
self.done = True
for attempt in try_until_success(attempts=10):
with attempt:
command1()
command2()
command3()您可能希望将上下文管理器从迭代器中分离出来(以帮助防止不正确的使用),但它所做的事情与您所追求的类似。
发布于 2020-11-10 14:19:55
有没有一种方法可以在Python中实现
try_until_success来实现我想做的事情?
是。你不需要让它成为上下文管理器。只需让它成为接受一个函数的函数:
def try_until_success(command, attempts=1):
for _ in range(attempts):
try:
return command()
except Exception as exc:
err = exc
raise err然后语法仍然很清楚,没有for或while语句,甚至没有with。
attempts = 10
try_until_success(command1, attempts)
try_until_success(command2, attempts)
try_until_success(command3, attempts)https://stackoverflow.com/questions/64770298
复制相似问题