There are two things involved here:
1. class attributes and instance attributes
2. difference between the operators + and += for lists
+
operator calls the __add__
method on a list. It takes all the elements from its operands and makes a new list containing those elements maintaining their order.
+=
operator calls __iadd__
method on the list. It takes an iterable and appends all the elements of the iterable to the list in place. It does not create a new list object.
In class foo
the statement self.bar += [x]
is not an assignment statement but actually translates to
self.bar.__iadd__([x]) # modifies the class attribute
which modifies the list in place and acts like the list method extend
.
In class foo2
, on the contrary, the assignment statement in the init
method
self.bar = self.bar + [x]
can be deconstructed as:
The instance has no attribute bar
(there is a class attribute of the same name, though) so it accesses the class attribute bar
and creates a new list by appending x
to it. The statement translates to:
self.bar = self.bar.__add__([x]) # bar on the lhs is the class attribute
Then it creates an instance attribute bar
and assigns the newly created list to it. Note that bar
on the rhs of the assignment is different from the bar
on the lhs.
For instances of class foo
, bar
is a class attribute and not instance attribute. Hence any change to the class attribute bar
will be reflected for all instances.
On the contrary, each instance of the class foo2
has its own instance attribute bar
which is different from the class attribute of the same name bar
.
f = foo2(4)
print f.bar # accessing the instance attribute. prints [4]
print f.__class__.bar # accessing the class attribute. prints []
Hope this clears things.