Python:用于实体突变的 ContextManager?

问题描述 投票:0回答:1

我想建立一个代码模式,允许我执行类似于以下内容的操作:

# 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 构建过此类框架吗?您喜欢什么解决方案?

python entity persistence
1个回答
0
投票

您可以创建一个“不可变”代理对象,通过引发异常来禁用 setter 和 deleter 方法。禁止通过实现上下文管理器来设置新属性,该上下文管理器在进入时返回原始可变对象以允许上下文内发生突变:

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'
© www.soinside.com 2019 - 2024. All rights reserved.