Python: understanding class and instance variables

I think I have some misconception about class and instance variables. Here is an example code:

    class Animal(object):
        energy = 10
        skills = []

        def work(self):
            print 'I do something'
            self.energy -= 1

        def new_skill(self, skill):
            self.skills.append(skill)


    if __name__ == '__main__':

        a1 = Animal()
        a2 = Animal()

        a1.work()
        print a1.energy  # result:9
        print a2.energy  # result:10


        a1.new_skill('bark')
        a2.new_skill('sleep')
        print a1.skills  # result:['bark', 'sleep']
        print a2.skills  # result:['bark', 'sleep']

I thought that energy and skill were class variables, because I declared them out of any method. I modify its values inside the methods in the same way (with self in his declaration, maybe incorrect?). But the results show me that energy takes different values for each object (like a instance variable), while skills seems to be shared (like a class variable). I think I've missed something important...

You are running into initialization issues based around mutability.

First , the fix. skills and energy are class attributes. It is a good practice to consider them as read only, as initial values for instance attributes. The classic way to build your class is:

    class Animal(object):
        energy = 10
        skills = []
        def __init__(self,en=energy,sk=skills):
            self.energy=en
            self.skills=sk
       ....

Then each instance will have its own attributes, all your problems will disappear.

Second , what's happening with this code? Why is skills shared, when energy is per-instance?

The -= operator is subtle. it is for in-place assignation if possible. The difference here is that list types are mutable so in-place modification often occurs:

    In [6]: 
       b=[]
       print(b,id(b))
       b+=['strong']
       print(b,id(b))

    [] 201781512
    ['strong'] 201781512

So a1.skills and a2.skills are the same list, which is also accessible as Animal.skills. But energy is a non-mutable int, so modification is impossible. In this case a new int object is created, so each instance manages its own copy of the energy variable:

    In [7]: 
         a=10
         print(a,id(a))
         a-=1
         print(a,id(a))

    10 1360251232
    9 1360251200

From: stackoverflow.com/q/35766834

Back to homepage or read more recommendations: