首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在Python中重复with语句的主体?

如何在Python中重复with语句的主体?
EN

Stack Overflow用户
提问于 2020-11-10 13:56:29
回答 3查看 370关注 0票数 6

我想实现一种只使用上下文管理器重复一段代码所需次数的方法,因为它的语法非常好。如下所示:

代码语言:javascript
复制
with try_until_success(attempts=10):
    command1()
    command2()
    command3()

如果没有发生错误,则必须执行一次命令。如果发生错误,则应该再次执行它们,直到10次尝试通过为止,如果是,则必须引发错误。例如,重新连接到数据库可能很有用。我表示的语法是字面的,我不想修改它(所以不要建议我用一种for of while语句来替换它)。

有什么方法可以在try_until_success中实现来做我想做的事情吗?

我试过的是:

代码语言:javascript
复制
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

这给了我一个错误:

代码语言:javascript
复制
RuntimeError: generator didn't stop after throw()

我知道,有很多方法可以使用循环而不是with-statement或者在装饰师的帮助下达到我所需要的。但两者都有语法上的缺点。例如,在循环的情况下,我必须插入try-except块,对于装饰器,我必须定义一个新函数。

我已看过以下问题:

如何制作一个内部有循环的contextmanager?

有条件地跳过带有语句的Python主体

他们对我的问题没有帮助。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2020-11-10 14:14:22

问题是with语句的主体没有在对try_until_success的调用中运行。该函数使用__enter__方法返回一个对象;__enter__方法调用并返回该对象,然后执行with语句的主体。没有关于将主体封装在任何类型的循环中的规定,这样就可以在到达with语句的结尾时重复它。

票数 2
EN

Stack Overflow用户

发布于 2020-11-10 14:06:33

这有悖于上下文管理器设计的工作方式,您可能不得不使用非标准的技巧,比如修补字节码。

有关如何展开这些文档,请参见带语句上的正式文档和原始佩普343。它可能会帮助你理解为什么这不会得到官方的支持,也可能帮助你理解为什么其他的评论者通常会说这是一件坏事。

作为可能奏效的例子,不妨尝试:

代码语言:javascript
复制
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()

您可能希望将上下文管理器从迭代器中分离出来(以帮助防止不正确的使用),但它所做的事情与您所追求的类似。

票数 2
EN

Stack Overflow用户

发布于 2020-11-10 14:19:55

有没有一种方法可以在Python中实现try_until_success来实现我想做的事情?

是。你不需要让它成为上下文管理器。只需让它成为接受一个函数的函数:

代码语言:javascript
复制
def try_until_success(command, attempts=1):
    for _ in range(attempts):
        try:
            return command()
        except Exception as exc:
            err = exc

    raise err

然后语法仍然很清楚,没有forwhile语句,甚至没有with

代码语言:javascript
复制
attempts = 10
try_until_success(command1, attempts)
try_until_success(command2, attempts)
try_until_success(command3, attempts)
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64770298

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档