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
+=
returns an object that is reassigned to the left operand. But assignment statements are not expressions, so you can't use them as a value. – Prussiatea += b
is a statement, not an expression. You can notprint
it like you can notprint(a = a + b)
– Shouldst+=
return something that cannot be used as a expression like in C/C++. The implementations of__add__
and__iadd__
are almost identical but why one of them can be used as a expression, the other one can't? – Prelate+
would be useless if it didn't give you an expression. The purpose of+=
is to update the left operand. It's part of Python's design that actions to do one thing don't incidentally do something else as well. – Prussiatea
in-place and print the result:a = 1 ; b = 2 ; print(a := a + b) ; print(a)
gives the output3 ; 3
– Shouldst__iadd__
does return something. And it doesn't mention that augmented assignments are statements instead of expressions. – Prelate+=
"inplace". The value inself
is changed anyway. So I guess Python uses the returned object for the "final" assignment, since it will returnNone
by default. Anyway, in other languages like C++, it won't destroy your object completely if you forget to return itself in a normal method. – Prelate+=
statement doesn't return anything, because it isn't an expression. The hook__iadd__
does, because that hook is used by Python to control the behavior of+=
– Lipid__iadd__
and__add__
are not one-to-one equivalent with+=
and+
. They are hooks – Lipid__iadd__
remains a statement, not an expression" no.__iadd__
is a method, that method is used as a hook when the statement ` a += b` is executed. It doesn't even have to exist for that statement to evaluate just fine (e.g., it will fall back to the__add__
hook if not implemented) – Lipid:=
operator. I'm forced to stay with Python3.5 due to some CUDA stuff. As you can see I didn't even use a f-string. But it's good to know that Python is finally equipped with an assignment expression. – Prelate