W grę wchodzą dwie rzeczy:
1. class attributes and instance attributes
2. difference between the operators + and += for lists
+
operator wywołuje __add__
metodę z listy. Pobiera wszystkie elementy ze swoich operandów i tworzy nową listę zawierającą te elementy, zachowując ich kolejność.
+=
__iadd__
metoda wywołania operatora na liście. Pobiera iterowalne i dołącza wszystkie elementy iterowalne do listy w miejscu. Nie tworzy nowego obiektu listy.
W klasie foo
instrukcja self.bar += [x]
nie jest instrukcją przypisania, ale w rzeczywistości jest tłumaczeniem
self.bar.__iadd__([x]) # modifies the class attribute
który modyfikuje listę w miejscu i działa jak metoda list extend
.
W klasie foo2
wręcz przeciwnie, instrukcja przypisania w init
metodzie
self.bar = self.bar + [x]
można zdekonstruować jako:
Instancja nie ma atrybutu bar
(istnieje jednak atrybut klasy o tej samej nazwie), więc uzyskuje dostęp do atrybutu klasy bar
i tworzy nową listę, dołączając x
do niej. Oświadczenie przekłada się na:
self.bar = self.bar.__add__([x]) # bar on the lhs is the class attribute
Następnie tworzy atrybut instancji bar
i przypisuje do niego nowo utworzoną listę. Zauważ, że bar
prawa strona zadania różni się od bar
prawej strony.
Dla instancji klasy foo
, bar
jest atrybutem klasy i nie atrybut instancji. W związku z tym każda zmiana atrybutu klasy bar
zostanie odzwierciedlona we wszystkich instancjach.
Wręcz przeciwnie, każda instancja klasy foo2
ma swój własny atrybut instancji, bar
który różni się od atrybutu klasy o tej samej nazwie bar
.
f = foo2(4)
print f.bar # accessing the instance attribute. prints [4]
print f.__class__.bar # accessing the class attribute. prints []
Mam nadzieję, że to wszystko wyjaśnia.