This answer is based on the Answer by Martijn Pieters, I found it useful, but I updated it to fit better with Python3.
Like the accepted answer I've overridden the __setattr__
special method, but we save most of the complexity by just using super to keep the original behavior of the method.
def ChangeMonitor(cls):
def __setattr__(self, name, value):
try:
original_value = getattr(self, name)
if original_value == value:
print(f"No Change for Value {name}")
else:
print(f"Value {name} has changed from {original_value} to {value}")
except AttributeError:
print(f"Initializing Value {name} with {value}")
super(cls, self).__setattr__(name, value)
cls.__setattr__ = __setattr__
return cls
Demo:
>>> changer = ChangingClass(5)
Initializing Value x with 5
>>> changer.x = 6
Value x has changed from 5 to 6
>>> changer.x = None
Value x has changed from 6 to None
>>> changer.x = 2
Value x has changed from None to 2
>>> changer.x += 8
Value x has changed from 2 to 10
There are a few edge cases that if you're relying on this you should consider.
If your datatype is mutable you can still change the underlying data without triggering setattr.
>>> changer = ChangingClass([5])
Initializing Value x with [5]
>>> changer.x.append(10)
>>> changer.x = []
Value x has changed from [5, 10] to []
In addition class variables changing will not trigger your setattr method. And if you override the class variable with an instance variable, it won't register as initializing.
>>> changer = ChangingClass(1)
Initializing Value x with 1
>>> changer.z = 9
Initializing Value z with 9
>>> ChangingClass.y = 2
>>> changer.y = 6
Value y has changed from 2 to 6