I was fiddling around with inheritance recently and I'm a little confused by the behaviour of the following:
class Foo(list):
def method(self, thing):
new = self + [thing]
print(new)
self = new
print(self)
def method2(self, thing):
self += [thing]
>>> f = Foo([1, 2, 3, 4, 5])
>>> f.method(10)
[1, 2, 3, 4, 5, 10]
[1, 2, 3, 4, 5, 10]
>>> f
[1, 2, 3, 4, 5]
>>> f.method2(10)
>>> f
[1, 2, 3, 4, 5, 10]
Why does the in-place method method2
work but the first one doesn't?
Because that's how in-place operators work.
self = self + [thing]
creates a new list and put it into the local variable self
, overriding the passed one. But it doesn't modify the object itself. Internally, it does self = self.__add__([thing])
.
self += [thing]
, OTOH, modifies the list in-place. Internally it tries self = self.__iadd__([thing])
first. iadd
stands for "inplace add". Only if that doesn't exist, self = self.__add__([thing])
is called.
The difference is that __add__()
always creates a new object and leaves the others untouched. __iadd__()
, however, is supposed to try first to modify the object it operates on. In this case, it returns it so that no change of object occurs, i. e. self
refers to the same object as before. Only if this is not possible, it returns a new one which is then assigned.
list.__add__()
(the +
operator builtin) creates a new list, while list.__iadd__()
(the +=
operator builtin) modifies the list in place.