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