2

I don't understand the difference in the following example. One time an instance of a class can CHANGE the class variable of another instance and the other time it can't?

Example 1:

class MyClass(object):
    mylist = []

    def add(self):
        self.mylist.append(1)

x = MyClass()
y = MyClass()
x.add()
print "x's mylist: ",x.mylist
print "y's mylist: ",y.mylist

Output:

x's mylist: [1]

y's mylist: [1]

So here an instance x of class A was able to access and modify the class attribute mylist which is also an attribute of the instance y of A.

Example 2:

class MyBank(object):
    crisis = False

    def bankrupt(self) :
        self.crisis = True

bankX = MyBank()
bankY = MyBank()
bankX.bankrupt()
print "bankX's crisis: ",bankX.crisis
print "bankY's crisis: ",bankY.crisis

bankX's crisis: True

bankY's crisis: False

Why does this not work in this example?

Pablo
  • 4,821
  • 12
  • 52
  • 82
uitty400
  • 277
  • 1
  • 3
  • 14
  • 5
    This is the same old "mutable vs. immutable" question we get 5 times a day (plus a 2nd reason why it doesn't work that way) and it gets 5 upvotes just because it involves classes? Congratulations, I've just lost all faith in the SO community. – Aran-Fey Aug 19 '16 at 13:14
  • myList is class variable and is mutable – FujiApple Aug 19 '16 at 13:15
  • See [python documentation](https://docs.python.org/3.5/tutorial/classes.html#class-and-instance-variables) which has almost exactly the same example. – Tim Fuchs Aug 19 '16 at 13:16
  • as @TimFuchs says, try "wrapping" the `mylist` attribute inside a `def __init__(self):` as `self.mylist` to make it instance specific – Ma0 Aug 19 '16 at 13:19
  • @Martijn Pieters The answer you linked is not answering the question. – uitty400 Aug 19 '16 at 13:23
  • @uitty400: yes, it does. Choice quote from one of the answers: *However, the confusing scenario in the question will never happen to immutable objects such as numbers and strings, because their value cannot be changed without assignment.*, – Martijn Pieters Aug 19 '16 at 13:25
  • The simple rule to remember is: *if a property cannot be found on the instance, it is looked up on the class.* `self.x = …` sets a property on the instance, `self.x…` accesses a property on the instance *or class*. – deceze Aug 19 '16 at 13:26

1 Answers1

6

In first case there is no assignment in add method:

def add(self):
    self.mylist.append(1)  # NOT self.mylist = something

In second case there is an assignment:

def bankrupt(self) :
    self.crisis = True  # self.crisis = something

When an attribute is set on instance, it is always set on particular instance only (it's put to instance's __dict__ atribute). Class __dict__ is unaffected.

In first case there is no assignment, so standard look-up rules apply. Since there is no "mylist" in __dict__ attribute of instance, it falls back to class __dict__.

Operation performed in add mutates value stored in MyClass.__dict__. That's why change is observable in all instances.

Consider following snippet (it may explain your issue better):

class MyClass:
    x = []

x1 = MyClass()
x2 = MyClass()
x3 = MyClass()

x1.x.append(1)

print x1.x  # [1]
print x2.x  # [1]
print x3.x  # [1]
assert x1.x is x2.x is x3.x

x3.x = "new"  # now x3.x no longer refers to class attribute

print x1.x # [1]
print x2.x # [1]
print x3.x # "new"
assert x1.x is x3.x  # no longer True!
Łukasz Rogalski
  • 22,092
  • 8
  • 59
  • 93