So I came across this nasty bug today in my code. I've just recently been using Python properties, and used it to preserve the old value of the property in another variable whenever the property was updated. So when the setter function is called, I needed the old value to save, and then the new value to transfer.
It turns out that when using +=
on a property, the __iadd__
method is called, updating the property in-place, then the setter is called. But by that time the old value is lost. Here's some code illustrating the problem.
class Container(object):
def __init__(self, val):
self.val = val
def __add__(self, other):
print('in __add__', other)
return Container(self.val + other.val)
def __iadd__(self, other):
print('in __iadd__:', other)
self.val += other.val
return self
def __str__(self):
return str(self.val)
class Test:
def __init__(self):
self._val = Container(0)
@property
def val(self):
return self._val
@val.setter
def val(self, values):
print("changing from {} to {}".format(self._val, values))
self._val = values
test = Test()
print('val=', test.val)
test.val = Container(2)
print('val=', test.val)
test.val = test.val + Container(1)
print('val=', test.val)
test.val += Container(1)
print('val=', test.val)
test.val.__iadd__(Container(1))
print('val=', test.val)
When run it gives this result:
val= 0
changing from 0 to 2
val= 2
in __add__ 1
changing from 2 to 3
val= 3
in __iadd__: 1
changing from 4 to 4
val= 4
in __iadd__: 1
val= 5
It says it's changing 4 to 4 but it is really changing 3 to 4 and that was missed. So I'm wondering if this is a bug, I'm missing something, or I am just not supposed to use += if I'm dealing with a property and need the old value.