Martin - 1 year ago 48

Python Question

I am designing an object-oriented data structure that shall be simple to handle from the user's perspective, e.g., by method chaining (aka Fluent interface). However, each change should only act *temporarily* on the object: within that chain, but not beyond that chain.

Here is a simplified example that does *not* work as intended:

`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)

In this case,

`.add()`

`number`

`# This is the only way how a user should make permanent changes:`

number = number.add(4).add(12)

In the

`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

However, the actuall classes and objects with which I am working contain a huge amount of data, attributes, methods, and even subclasses. So we should avoid copying the instance in every single method, besides the fact that it involves ugly code.

Are there ways to solve this problem, e.g. by (silently) creating a single copy only once at the first element of the chain? Or by destroying any changes made at the end of the chain?

An accepted solution should perform the necessary operations

`# These are NOT accepted solutions:`

number.copy().add(4).add(12)

number.add(4).add(12).undo()

If there no direct solution other than self-replication, the question would be: What is the most elegant way to do it that sustains code-readability and keeps memory usage low? E.g., decorating every class method by self-replicating function?

Answer Source

Instead of modyfing the object on which you call the method, return a modified copy:

```
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
```

For details on memory handling in this solution, see comments of this posts. This solution is standard way of solving your problem.