Python 一个类中有多个上下文管理器

标签 python with-statement

我希望能够编写这样的代码:

with obj.in_batch_mode:
    obj.some_attr = "some_value"
    obj.some_int = 142
    ...

当我希望 obj 等待发送有关其自身的更新,直到多个作业完成时。我在 __setattr__ 上有一些钩子(Hook),需要一些时间才能运行,并且可以将更改一起发送。

我不想使用这样的代码,因为它增加了忘记离开batch_mode的风险(这是 with 关键字的优点):

obj.enter_batch_mode()
obj.some_attr = "some_value"
obj.some_int = 142
...
obj.exit_batch_mode()

我一直无法弄清楚如何实现这一点。仅仅输入 with obj: (并且简单地在 obj 上实现 with)不会读到任何接近描述性的内容。

最佳答案

通常,实现上下文管理器的一个非常简单的方法是使用 contextlib模块。编写上下文管理器变得就像编写单个yield 生成器一样简单。在yield取代__enter__方法之前,yield出来的对象是__enter__的返回值,yield之后的部分就是__exit__方法。类上的任何函数都可以是上下文管理器,它只需要这样装饰即可。例如,采用这个简单的 ConsoleWriter 类:

from contextlib import contextmanager

from sys import stdout
from io import StringIO
from functools import partial

class ConsoleWriter:

    def __init__(self, out=stdout, fmt=None):
        self._out = out
        self._fmt = fmt

    @property
    @contextmanager
    def batch(self):
        original_out = self._out
        self._out = StringIO()
        try:
            yield self
        except Exception as e:
            # There was a problem. Ignore batch commands.
            # (do not swallow the exception though)
            raise
        else:
            # no problem
            original_out.write(self._out.getvalue())
        finally:
            self._out = original_out

    @contextmanager
    def verbose(self, fmt="VERBOSE: {!r}"):
        original_fmt = self._fmt
        self._fmt = fmt
        try:
            yield self
        finally:
            # don't care about errors, just restore end
            self._fmt = original_fmt

    def __getattr__(self, attr):
        """creates function that writes capitalised attribute three times"""
        return partial(self.write, attr.upper()*3)


    def write(self, arg):
        if self._fmt:
            arg = self._fmt.format(arg)
        print(arg, file=self._out)

使用示例:

writer = ConsoleWriter()
with writer.batch:
    print("begin batch")
    writer.a()
    writer.b()
    with writer.verbose():
        writer.c()
    print("before reentrant block")
    with writer.batch:
        writer.d()
    print("after reentrant block")
    print("end batch -- all data is now flushed")

输出:

begin batch
before reentrant block
after reentrant block
end batch -- all data is now flushed
AAA
BBB
VERBOSE: 'CCC'
DDD

关于Python 一个类中有多个上下文管理器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31505946/

相关文章:

boto 过滤器中的 Python 比较运算符

python - 如何防止库修改根记录器?

python - 如何使用 __enter__/__exit__ 方法对类进行单元测试?

excel - 用 "With"和 "Do While"替换 Select 语句后,VBA 运行速度非常慢

python - 对于在 python 中使用 `with` 关键字的类,我可以让它 __repr__ (打印)本身吗?

python - Pandas - 如何在应用函数内识别 nans?

python - 如何在创建 Dataproc 集群时运行 Bash 脚本作为初始化操作?

java - 将输入集放入图形拼图的数据结构中,然后求解(python 或 java)

vba - 是否可以在With Rg中引用Rg

python - with 语句的等效 try 语句是什么?