在方法链接期间保持对对象的临时更改

时间:2016-11-16 09:55:05

标签: python class oop fluent fluent-interface

我正在设计一个面向对象的数据结构,从用户的角度来看,它很容易处理,例如,通过方法链(aka Fluent interface)。但是,每次更改只应在对象上执行暂时:在该链中,但不能超出该链。
以下是 not 按预期工作的简化示例:

class C:
    def __init__(self, num):
        self.sum = num

    def add(self, num=0):
        self.sum += num
        return self

number = C(23)
number.add(7).add(12).sum
# 42
number.sum
# 42 (but I expect: 23)

在这种情况下,.add()会将永久更改为number。但是,永久性更改应该是这样的:

# This is the only way how a user should make permanent changes:
number = number.add(4).add(12)

临时范围内,我正在寻找一种在链终止后返回旧版number的方法。在绝望的边缘,我可以想到丑陋的解决方案,如"实例复制":

class C2:
    def __init__(self, num):
        self.sum = num

    def add(self, num=0):
        self2 = C2(self.sum) # or equivalently: self2 = copy.deepcopy(self)
        self2.sum += num
        return self2

number = C2(23)
number.add(7).add(12).sum
# 42
number.sum
# 23

但是,我正在使用的实际类和对象包含大量数据,属性,方法甚至子类。所以我们应该避免在每个方法中复制实例,除了它涉及丑陋的代码。

有没有办法解决这个问题,例如通过(静默地)在链的第一个元素上只创建一个副本一次?或者通过销毁在链末端做出的任何改变? (请注意,真实世界"更改"涉及许多不同的,可通信的方法,而不仅仅是添加数字)

接受的解决方案应该在内部执行必要的操作,即不用打扰用户界面:

# These are NOT accepted solutions:
number.copy().add(4).add(12)
number.add(4).add(12).undo()

如果除了自我复制之外没有直接的解决方案,那么问题是:最优雅的方法是什么来维持代码可读性并保持较低的内存使用率?例如,通过自我复制功能装饰每一类方法?

1 个答案:

答案 0 :(得分:1)

不是修改调用方法的对象,而是返回修改后的副本:

class C:
    def __init__(self, num):
        self.sum = num

    def add(self, num=0):
        return C(self.sum + num)

number = C(23)
assert number.add(7).add(12).sum == 42
assert number.sum == 23

有关此解决方案中内存处理的详细信息,请参阅此帖子的评论。此解决方案是解决问题的标准方法。