Python:实体变更的上下文管理器?
我想建立一个代码模式,让我能做一些类似下面的事情:
# Foo has getter-only properties
foo: Foo = FooRepo.get(id="foo_id")
assert foo.bar == "original_bar"
# MutableFoo is a window onto the Foo, and when it __exit__s, it persists to the repo
with foo.mutable() as mutable_foo:
mutable_foo.bar = "new_bar"
# We've updated the Foo, as well as whatever persistent version the FooRepo owns
assert foo.bar == "new_bar"
我对具体的代码模式并不执着。我喜欢这个模式的原因有:
- 我们可以把
Foo
传递到很多代码的地方,只要不调用mutable()
,我们就可以把它当作不可变的,忽略持久化的问题。 - 我们可以在
ContextManager
中以多种方式处理事务性。离开ContextManager
后,我们可以把这个对象当作快照,这样使用起来更常见,也更简单。 - 调用者基本上可以忽略持久化的事情。
我看到的一些挑战:
- 需要一种优雅的方法来防止在
with
块外创建可变版本。 - 同样需要一种方法来处理与
Foo
的接口,这样MutableFoo
才能进行修改。(你能看出来我习惯用 Java 吗?没有内部类来访问私有成员让我有点困惑) - 需要一种优雅的方法来进行错误检查。由于持久化是在退出上下文时发生的,这可能会出现异常,我们需要妥善处理这些异常。
有没有人用 Python 建立过这种框架?你们喜欢什么解决方案?
2 个回答
1
你可以创建一个“不可变”的代理对象,这个对象会让设置值和删除值的方法抛出异常,也就是说不允许修改。你可以实现一个上下文管理器,这样在进入这个上下文时,可以返回原来的可变对象,以便在这个上下文中进行修改:
def protected(obj):
class Protected(type(obj)):
def __getattr__(self, name):
return getattr(obj, name)
def __setattr__(self, name, value):
raise AttributeError("can't set attribute")
def __delattr__(self, name):
raise AttributeError("can't delete attribute")
def __enter__(self):
return obj
def __exit__(self, exc_type, exc_val, exc_tb):
pass
return object.__new__(Protected)
这样,下面的代码就能通过检查:
class Foo:
def __init__(self, bar):
self.bar = bar
foo = protected(Foo('original_bar'))
with foo as mutable_foo:
mutable_foo.bar = 'new_bar'
assert foo.bar == 'new_bar'
而下面的代码则会抛出一个 AttributeError
错误:
foo.bar = 'new_bar'
1
我觉得除非必要,否则最好不要使用包装类。用一个简单的布尔值就能搞定,感觉会简单很多。
class Foo:
def __init__(self, bar):
self._bar = bar
self._mutable = False
@property
def bar(self):
return self._bar
@bar.setter
def bar(self, value):
if not self._mutable:
raise RuntimeError('No touchy')
self._bar = value
@contextlib.contextmanager
def mutable(self):
self._mutable = True
try:
yield
finally:
self._mutable = False
foo = Foo('sup')
foo.bar = 'bro' # RuntimeError
with foo.mutable():
foo.bar = 'bro'
因为所有的修改都是直接在Foo对象上进行的,所以不需要特别的操作,修改的内容会自动在你的FooRepo中反映出来。