A class variable is a variable that defines a particular property or attribute for a class. An instance variable is a variable whose value is specified to the Instance and shared among different instances. 2. We can share these variables between class and its subclasses. We cannot share these variables between classes.
What is an Instance Variable in Python? If the value of a variable varies from object to object, then such variables are called instance variables. For every object, a separate copy of the instance variable will be created. Instance variables are not shared by objects.
Let's start on class and instance variables. Class variables are declared inside a class but outside of any function. Instance variables are declared inside the constructor which is the __init__method.
Class variables also known as static variables are declared with the static keyword in a class, but outside a method, constructor or a block. Instance variables are created when an object is created with the use of the keyword 'new' and destroyed when the object is destroyed.
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 self.energy
.
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.
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=None):
self.energy = en
self.skills = [] if sk is None else 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
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
The 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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With