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
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.
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?
skills shared, when
energy is per-instance?
-= 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 : b= print(b,id(b)) b+=['strong'] print(b,id(b))  201781512 ['strong'] 201781512
a2.skills are the same list, which is also accessible as
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
In : a=10 print(a,id(a)) a-=1 print(a,id(a)) 10 1360251232 9 1360251200
The trick here is in understanding what
self.energy -= 1 does. It’s really two expressions; one getting the value of
self.energy - 1, and one assigning that back to
But the thing that’s confusing you is that the references are not interpreted the same way on both sides of that assignment. When Python is told to get
self.energy, it tries to find that attribute on the instance, fails, and falls back to the class attribute. However, when it assigns to
self.energy, it will always assign to an instance attribute, even though that hadn’t previously existed.
Upon initial creation both attributes are the same object:
>>> a1 = Animal() >>> a2 = Animal() >>> a1.energy is a2.energy True >>> a1.skills is a2.skills True >>> a1 is a2 False
When you assign to a
class attribute, it is made local to the instance:
>>> id(a1.energy) 31346816 >>> id(a2.energy) 31346816 >>> a1.work() I do something >>> id(a1.energy) 31346840 # id changes as attribute is made local to instance >>> id(a2.energy) 31346816
new_skill() method does not assign a new value to the
skills array, but rather it
appends which modifies the list in place.
If you were to manually add a skill, then the
skills list would be come local to the instance:
>>> id(a1.skills) 140668681481032 >>> a1.skills = ['sit', 'jump'] >>> id(a1.skills) 140668681617704 >>> id(a2.skills) 140668681481032 >>> a1.skills ['sit', 'jump'] >>> a2.skills ['bark', 'sleep']
Finally, if you were to delete the instance attribute
a1.skills, the reference would revert back to the class attribute:
>>> a1.skills ['sit', 'jump'] >>> del a1.skills >>> a1.skills ['bark', 'sleep'] >>> id(a1.skills) 140668681481032
Access the class variables through the class, not through self:
class Animal(object): energy = 10 skills =  def work(self): print 'I do something' self.__class__.energy -= 1 def new_skill(self, skill): self.__class__.skills.append(skill)
Actually in you code
when you are calling a1.work() an instance variable for a1 object is getting created with the same name that is ‘energy’.
And When interpreter comes to ‘print a1.energy’ it execute the instance variable of object a1.
And when interpreter comes to ‘print a2.energy’ it execute the class variable, and since you have not changed the value of class variable it shows 10 as output.