I'm having a problem understanding how class / instance variables work in Python. I don't understand why when I try this code the list variable seems to be a class variable
class testClass():
list = []
def __init__(self):
self.list.append('thing')
p = testClass()
print p.list
f = testClass()
print f.list
Output:
['thing']
['thing', 'thing']
and when I do this it seems to be an instance variable
class testClass():
def __init__(self):
self.list = []
self.list.append('thing')
p = testClass()
print p.list
f = testClass()
print f.list
Output:
['thing']
['thing']
4 Answers 4
This is because of the way Python resolves names with the .. When you write self.list the Python runtime tries to resolve the list name first by looking for it in the instance object, and if it is not found there, then in the class instance.
Let's look into it step by step
self.list.append(1)
- Is there a
listname into the objectself?- Yes: Use it! Finish.
- No: Go to 2.
- Is there a
listname into the class instance of objectself?- Yes: Use it! Finish
- No: Error!
But when you bind a name things are different:
self.list = []
- Is there a
listname into the objectself?- Yes: Overwrite it!
- No: Bind it!
So, that is always an instance variable.
Your first example creates a list into the class instance, as this is the active scope at the time (no self anywhere). But your second example creates a list explicitly in the scope of self.
More interesting would be the example:
class testClass():
list = ['foo']
def __init__(self):
self.list = []
self.list.append('thing')
x = testClass()
print x.list
print testClass.list
del x.list
print x.list
That will print:
['thing']
['foo']
['foo']
The moment you delete the instance name the class name is visible through the self reference.
6 Comments
self.list[] outside of init, this makes sense but is there a way?__new__ and create them there, but that's really ugly as it's not what __new__ is really for, and it's not really any better than creating them in __init__. What, pray-tell, do you think you need this ability for? You could create a meta-class that overloads __new__ to look through the class's dict and copy things to the instance dict. But that's really ugly.p.list = [], outside any function, in the global scope and it will just work.class construct is just syntactic sugar for a bunch of assignments to members of a class instance. The closest thing to a declaration is an assignment to the class' __slots__ variable for a new-style class.Python has interesting rules about looking up names. If you really want to bend your mind, try this code:
class testClass():
l = []
def __init__(self):
self.l = ['fred']
This will give each instance a variable called l that masks the class variable l. You will still be able to get at the class variable if you do self.__class__.l.
The way I think of it is this... Whenever you do instance.variable (even for method names, they're just variables who's values happen to be functions) it looks it up in the instance's dictionary. And if it can't find it there, it tries to look it up in the instance's class' dictionary. This is only if the variable is being 'read'. If it's being assigned to, it always creates a new entry in the instance dictionary.
1 Comment
In your first example, list is an attribute of the class, shared by all instances of it. This means that you can even access it without having an object of type testClass:
>>> class testClass():
... list = []
... def __init__(self):
... self.list.append("thing")
...
>>> testClass.list
[]
>>> testClass.list.append(1)
>>> testClass.list
[1]
But all objects share the list attribute with the class and each other:
>>> testObject = testClass()
>>> testObject.list
[1, 'thing']
>>> testClass.list
[1, 'thing']
>>>
>>> testObject2 = testClass()
>>> testClass.list
[1, 'thing', 'thing']
>>> testObject2.list
[1, 'thing', 'thing']
Comments
When you instantiate a class, __init__ method is automatically executed.
In the first case your list is a class attribute and is shared by all its instances. You got two 'thing's because you appended one when instantitating p and another when instantiated f (the first one was already appended at the first call).
MyClass. You then went on to privatize the contents of a shared trait in each member of the classMyClass.