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

Python在一个类中有多个上下文管理器,python,with-statement,Python,With Statement,我希望能够编写如下代码: with obj.in_batch_mode: obj.some_attr = "some_value" obj.some_int = 142 ... 当我想obj等待发送关于自身的更新,直到多个作业完成时。我在\uuuu setattr\uuuuu上有一些钩子,它们需要一些时间才能运行,并且可以一起发送更改 我不想使用这样的代码,因为这会增加忘记离开batch_模式的风险(这是with关键字的好处): 我还没有弄清楚如何实现这一点。仅仅用ob

我希望能够编写如下代码:

with obj.in_batch_mode:
    obj.some_attr = "some_value"
    obj.some_int = 142
    ...
当我想
obj
等待发送关于自身的更新,直到多个作业完成时。我在
\uuuu setattr\uuuuu
上有一些钩子,它们需要一些时间才能运行,并且可以一起发送更改

我不想使用这样的代码,因为这会增加忘记离开batch_模式的风险(这是
with
关键字的好处):


我还没有弄清楚如何实现这一点。仅仅用obj:键入
(并在
obj
上简单地用
实现
)并不能理解为具有描述性。

可能是这样的:

with obj.in_batch_mode:
    obj.some_attr = "some_value"
    obj.some_int = 142
    ...
实现助手类

class WithHelperObj(object):
    def __init__(self,obj):
        self.obj = obj

    def __enter__(self):
        self.obj.impl_enter_batch()

    def __exit__(self, exc_type, exc_value, traceback):
        self.obj.impl_exit_batch()

    class MyObject(object):
        def in_batch_mode(self):
            return WithHelperObj(self)
在类本身中,实现方法而不是字段,以便与with语句一起使用

    def impl_enter_batch(self):
        print 'In impl_enter_batch'

    def impl_exit_batch(self):
        print 'In impl_exit_batch'

    def doing(self):
        print 'doing'
然后使用它:

o = MyObject()
with o.in_batch_mode():
    o.doing()

如果您正在寻找一个简单的解决方案,并且不需要任何嵌套模式更改(例如从STD到批次再到详细再到批次再到STD)

输出

std
batch
std

这建立在Pynchia的答案之上,但增加了对多种模式的支持,并允许使用
语句嵌套
,即使多次使用相同的模式。它缩放
O(#嵌套的_模式)
,基本上是
O(1)

请记住使用堆栈存储与模式相关的数据

class A():
    _batch_mode = "batch_mode"
    _mode_stack = []

    @property
    def in_batch_mode(self):
        self._mode_stack.append(self._batch_mode)
        return self

    def __enter__(self):
        return self

    def __exit__(self, type, value, tb):
        self._mode_stack.pop()
        if self._batch_mode not in self._mode_stack:
            self.apply_edits()
然后我在任何需要的地方都有这些支票:

if self._batch_mode not in self._mode_stack:
    self.apply_edits()

也可以使用以下模式的方法:

with x.in_some_mode(my_arg):
只需记住在
x
中的堆栈中保存
my_arg
,并在从模式堆栈中弹出该模式时将其从堆栈中清除即可


使用此对象的代码现在可以

with obj.in_batch_mode:
    obj.some_property = "some_value"
嵌套也没有问题,因此我们可以使用obj添加另一个
。在某种模式下:
无论在何处都不会出现任何难以调试的错误,或者必须检查调用的每个函数,以确保对象的
with
-语句从不嵌套:

def b(obj):
    with obj.in_batch_mode:
        obj.some_property = "some_value"

x = A()
with x.in_batch_mode:
    x.my_property = "my_value"
    b(x)

通常,实现上下文管理器的一种非常简单的方法是使用该模块。编写上下文管理器变得像编写单个产量生成器一样简单。在yield替换
\uuuuuuuuuuuuuuuuuuuuuuuuuuuu
方法之前,yield对象是
\uuuuuuuuuuuuuuuuuu
方法的返回值,而yield之后的部分是
\uuuuuuuuuuuuuuuuuuuuuuuu
方法。类上的任何函数都可以是上下文管理器,它只需要对上下文进行这样的修饰。例如,以这个简单的
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

为了使用内置with语句,您需要实现enter和exit函数。请阅读整个问题:)您所说的“多个上下文管理器”是什么意思?我想在不同的属性上实现多个上下文管理器,这样我就可以在批处理模式下使用obj编写
,在详细模式下使用obj编写
,而不是在上面的示例中使用obj编写
,在
\uuuu enter\uuuuu
\uuu exit\uuu
方法中,对象中的某些模式状态会影响对象的行为方式吗?由于模式不一定包含在带有obj:
调用的
中,因此它不会像您希望的那样明确……通过使用模式列表或位图,可以很容易地修改为允许多种模式
begin batch
before reentrant block
after reentrant block
end batch -- all data is now flushed
AAA
BBB
VERBOSE: 'CCC'
DDD