我希望能够编写这样的代码:
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/