2

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

Martijn Pieters
1.1m324 gold badges4.2k silver badges3.4k bronze badges
asked May 9, 2015 at 9:47
15
  • 3
    They are not treated differently by Python. You treated them differently, by assigning in one place, but using a list method in another. Commented May 9, 2015 at 9:55
  • Try using self.tricks = self.tricks + [tricks] to see the difference. Commented May 9, 2015 at 9:56
  • 1
    Relevant and related: How do I avoid having Python class data shared among instances? Commented May 9, 2015 at 9:58
  • Also, looks like you're a bit confused about terminology. There're no immutable variables involded in your example (except for strings, but it's essentially irrelevant). Proper terms would be instance (name) and shared (kind and tricks) attributes. Instance attributes are unique to invividual object (instance of the class), while shared are shared among all instances of the class. Commented May 9, 2015 at 10:13
  • 1
    += is augmented assignment and gives the object the chance to update in-place; for a list object that uses list.extend(). Commented May 9, 2015 at 11:19

1 Answer 1

3

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'}
answered May 9, 2015 at 10:36
0

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.