I am executing the following Python code :
class Pet :
kind = 'dog' # class variable shared by all instances
tricks = []
def __init__(self, name) :
self.name = name # instance variable unique to each instance
def changePet(self, newPet) :
self.kind = newPet
def addTricks(self, tricks) :
self.tricks.append(tricks)
pet1 = Pet('mypet')
pet2 = Pet('yourpet')
print 'pet1 kind ::: ', pet1.kind;print 'pet2 kind ::: ', pet2.kind
print 'pet1 name ::: ', pet1.name;print 'pet2 name ::: ', pet2.name
Pet.kind = 'cat'
print 'changed Pet.kind to cat'
print 'pet1 kind ::: ', pet1.kind;print 'pet2 kind ::: ', pet2.kind
#changing pet#1 kind does not change pet#2 kind
pet1.changePet('parrot')
print 'changed pet1.kind to parrot'
print 'pet1 kind ::: ', pet1.kind;print 'pet2 kind ::: ', pet2.kind
pet1.addTricks('imitate')
pet2.addTricks('roll over')
print 'pet1 tricks ::: ', pet1.tricks;print 'pet2 tricks ::: ', pet2.tricks
print Pet.__dict__
print pet1.__dict__
print pet2.__dict__
The output is as per the explanation I found on internet. The output is as follows
pet1 kind ::: dog
pet2 kind ::: dog
pet1 name ::: mypet
pet2 name ::: yourpet
changed Pet.kind to cat
pet1 kind ::: cat
pet2 kind ::: cat
changed pet1.kind to parrot
pet1 kind ::: parrot
pet2 kind ::: cat
pet1 tricks ::: ['imitate', 'roll over']
pet2 tricks ::: ['imitate', 'roll over']
{'__module__': '__main__', 'tricks': ['imitate', 'roll over'], 'kind': 'cat', 'addTricks': <function addTricks at 0xb71fa6bc>, 'changePet': <function changePet at 0xb71fa33c>, '__doc__': None, '__init__': <function __init__ at 0xb71fa144>}
{'kind': 'parrot', 'name': 'mypet'}
{'name': 'yourpet'}
Now my query is that why is a mutable class object treated differently than a non-mutable class object
1 Answer 1
I'm not sure that they treated differently. You are just performing different operations on them.
In the case of changePet
, you are assigning the previously undefined self.kind
to a value. Now when python tries to find pet1.kind
, it finds it immediately. When looking up pet2.kind
, it doesn't find it. It does, however, find find Pet.kind
so it returns that.
In the case of addTricks
, you are attempting to mutate self.tricks
. Since that doesn't exist, you are instead given a reference to Pet.tricks
. Therefore, when you call append()
on self.tricks
, you are effective calling Pet.tricks.append()
and not self.tricks.append()
.
To clear things up, this:
def changePet(self, newPet) :
self.kind = newPet
def addTricks(self, tricks) :
self.tricks.append(tricks)
is effectively equivalent to this:
def changePet(self, newPet) :
self.kind = newPet
def addTricks(self, tricks) :
Pet.tricks.append(tricks)
To demonstrate that that python is not treating mutables differently than non-mutables, we need change your methods so that they perform similar operations:
def changePet(self, newPet) :
self.kind = newPet
def addTricks(self, tricks):
# assign self.tricks to a new value where we previously only mutated it!
# note: list(self.tricks) returns a copy
self.tricks = list(self.tricks)
self.tricks.append(tricks)
Now when we run your code again, we get the following output:
pet1 kind ::: dog
pet2 kind ::: dog
pet1 name ::: mypet
pet2 name ::: yourpet
changed Pet.kind to cat
pet1 kind ::: cat
pet2 kind ::: cat
changed pet1.kind to parrot
pet1 kind ::: parrot
pet2 kind ::: cat
pet1 tricks ::: ['imitate']
pet2 tricks ::: ['roll over']
{'__module__': '__main__', 'tricks': [], 'kind': 'cat', 'addTricks': <function addTricks at 0x02A33C30>, 'changePet': <function changePet at 0x02A33BF0>, '__doc__': None, '__init__': <function __init__ at 0x02A33BB0>}
{'tricks': ['imitate'], 'kind': 'parrot', 'name': 'mypet'}
{'tricks': ['roll over'], 'name': 'yourpet'}
self.tricks = self.tricks + [tricks]
to see the difference.name
) and shared (kind
andtricks
) attributes. Instance attributes are unique to invividual object (instance of the class), while shared are shared among all instances of the class.+=
is augmented assignment and gives the object the chance to update in-place; for a list object that useslist.extend()
.