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 = 1
is 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 + y
is equivalent tox = x.__add__(y)
,x += y
is 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