Original Question
While I was trying to answer another person's question on stackoverflow about the difference between = and += in Python, I encountered the following problem:
class Foo:
def __init__(self, value, name="default"):
self.value = value
self.name = name
def __add__(self, that):
return Foo(self.value + that.value)
def __iadd__(self, that):
self.value = self.value + that.value
return self
def __str__(self):
return "name: {}, value: {:d}".format(self.name, self.value)
a = Foo(1, 'alice')
b = Foo(2, 'bob')
print(a+=b)
The last print call was not successful and gave me this:
File "<ipython-input-8-0faa82ba9e4a>", line 3
print(a+=b)
^
SyntaxError: invalid syntax
I don't know why this isn't working. Maybe it has something to do with the keyword argument passing mechanism? I just can't find any resource on this topic, since the overloaded __iadd__ method already returns a Foo object.
************** update ******************
If I change the __iadd__ method like this (just remove the return statement):
...
def __iadd__(self, that):
print("__iadd__ was called")
self.value = self.value + that.value
a = Foo(1, 'alice')
b = Foo(2, 'bob')
a += b
print(a) # Outputs: None
So, the final return statement in __iadd__ is indeed required. But it does not function as I thought (I come from a C/C++ background, so this behavior is a bit strange for me)
************************* 2nd Update ********************************
I almost forget that = in Python makes up a statement instead of an expression. The return statement in __iadd__ and my experience with other languages gives me an illusion that += could be used as an expression.
As stated in the Python documentation, __add__ is used to construct a new object. __iadd__ is designed for inplace modifications. But it all depends on the implementation. Although __iadd__ returns a object, this object is somehow "intercepted" by the language and reassigned to the left-hand operator, and, the final effect is, __iadd__ remains a statement, not an expression. Please correct me if I'm wrong. Also, I didn't find any resource confirming that += is a statement.
Summary
- A plain code, say
a = 1is an assignment statement. It's not allowed to be used as a function argument. However, keyword argument passing is not restricted by that:print('Hello world', end='')still works. -
x = x + yis equivalent tox = x.__add__(y),x += yis equivalent tox = x.__iadd__(y), check the doc for more details.
- An example:
class Foo:
def __init__(self, value, name="default"):
self.value = value
self.name = name
def __add__(self, that):
return Foo(self.value + that.value)
def __iadd__(self, that):
self.value = self.value + that.value
return self
def __str__(self):
return "name: {}, value: {:d}".format(self.name, self.value)
a = Foo(1, 'alice')
b = Foo(2, 'bob')
c = a + b # objects a and b are unchanged
a += b # object a is changed
print(c) # name: default, value: 3
print(a) # name: alice, value: 3