I want to create a class in Python that implements a __add__
object method that allows summing two objects of the same class. Let's call this class Indicator
and the two objects to sum ind1
and ind2
. The Indicator
object has only one property elements
, which is a dictionary of integer values.
My implementation of __add__
combines the elements
properties of two objects, eventually summing those values with the same key.
from __future__ import annotations
import copy
class Indicator:
def __init__(self, elements={}):
self.elements = elements
def __add__(self, other: Indicator):
new = copy.deepcopy(self)
new.values = {k: self.elements.get(k, 0) + other.elements.get(k, 0) for k in set(self.elements) | set(other.elements)}
return new
ind1 = Indicator({1:1,2:2,3:3})
ind2 = Indicator({1:1,2:2})
new = ind1 + ind2
print('ind1: ',ind1.elements)
print('ind2: ',ind2.elements)
print(new.elements) # {1: 2, 2: 4, 3: 3}
I would like __add__
to return an object whose elements
property gets updated as one or both objects in the summation get updated along the code flow.
For example,
ind1.elements[4] = 4
print(new.elements) # I would like this to be {1: 2, 2: 4, 3: 3, 4:4}
ind1.elements[1] = 3
print(new.elements) # I would like this to be {1: 4, 2: 4, 3: 3, 4:4}
How can I do it?
EDIT
First of all, let me thank you all the users who posted a comment/answer. Following the suggestions given in the comments and answers, I came up with the following solution. The idea is to add two lists as properties of Indicator
: self.adds
and self.linked
.
The list
self.adds
collects the addends of the summation. It gets filled up when__add__
is called. So, in the example below,ind1.adds is []
andind2.adds is []
since both objects don't arise from a sum. On the contrary,new.adds is [ind1,ind2]
The list
self.linked
collects all those object that needs to be updated wheneverself
gets updated. In the example below,ind1.linked is [new]
andind2.linked is [new]
.
I am not completely satisfied with this solution. For example, it fails to work if we sum up three objects and then modify one of them. I can try to fix the code, but I am wondering if I am doing something unconventional. Any thoughts? The code is the following
from __future__ import annotations
import copy
class Indicator:
def __init__(self, elements=None):
if elements is None:
self._elements = {}
else:
self._elements = elements
self.adds = []
self.linked = []
@property
def elements(self):
return self._elements
@elements.setter
def elements(self, value):
self._elements = value
for i in range(len(self.linked)):
el = self.linked[i]
el.update()
def update(self):
summation = self.adds[0]
for obj in self.adds[1:]:
summation = summation.__add__(obj)
self._elements = summation.elements
def __add__(self, other: Indicator):
new = copy.deepcopy(self)
self.linked.append(new)
other.linked.append(new)
new.adds = [self, other]
new._elements = {k: self.elements.get(k, 0) + other.elements.get(k, 0) for k in
set(self.elements) | set(other.elements)}
return new
ind1 = Indicator({1: 1, 2: 2, 3: 3})
ind2 = Indicator({1: 1, 2: 2})
new = ind1 + ind2
print('ind1: ', ind1.elements)
print('ind2: ', ind2.elements)
print(new.elements) # {1: 2, 2: 4, 3: 3}
ind1.elements = {0: 0, 1: 3}
print('Updating ind1: ',new.elements == (ind1+ind2).elements)
ind2.elements = {0: 0, 7: 9}
print('Updating ind2: ',new.elements == (ind1+ind2).elements)