Existem duas coisas envolvidas aqui:
1. class attributes and instance attributes
2. difference between the operators + and += for lists
+operador chama o __add__método em uma lista. Ele pega todos os elementos de seus operandos e faz uma nova lista contendo esses elementos mantendo sua ordem.
+=o operador chama o __iadd__método da lista. Ele pega um iterável e anexa todos os elementos do iterável à lista no local. Ele não cria um novo objeto de lista.
Na aula, fooa declaração self.bar += [x]não é uma declaração de atribuição, mas na verdade se traduz em
self.bar.__iadd__([x]) # modifies the class attribute
que modifica a lista no local e atua como o método de lista extend.
Na aula foo2, ao contrário, a instrução de atribuição no initmétodo
self.bar = self.bar + [x]
pode ser desconstruída como:
A instância não tem nenhum atributo bar(embora haja um atributo de classe com o mesmo nome), portanto, ela acessa o atributo de classe bare cria uma nova lista anexando xa ela. A declaração se traduz em:
self.bar = self.bar.__add__([x]) # bar on the lhs is the class attribute
Em seguida, ele cria um atributo de instância bare atribui a lista recém-criada a ele. Observe que barno rhs da atribuição é diferente do barlhs.
Para instâncias de classe foo, baré um atributo de classe e não um atributo de instância. Portanto, qualquer alteração no atributo de classe barserá refletida em todas as instâncias.
Pelo contrário, cada instância da classe foo2tem seu próprio atributo de instância, barque é diferente do atributo de classe do mesmo nome bar.
f = foo2(4)
print f.bar # accessing the instance attribute. prints [4]
print f.__class__.bar # accessing the class attribute. prints []
Espero que isso esclareça as coisas.