33

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']
Eugene Primako
  • 2,767
  • 9
  • 26
  • 35
jonathan topf
  • 7,897
  • 17
  • 55
  • 85
  • 1
    If I understand correctly, you are asking for rationale behind particular design decision regarding Python's syntax. For such questions, the best answer is either "because it fits with the language's spirit", or more blatantly: "ask its creator". – Xion Jan 02 '12 at 13:51
  • ok so variables are class variables by default, that was where my confusion stemmed from, that should have been my question, do variables default to class variables – jonathan topf Jan 02 '12 at 13:55
  • 5
    @jonathantopf No, variables are NOT class variables by default. In Python you do not declare varables. For that matter, they are not actually variables (python is a _variableless_ lanuage), just names. And you do not declare or assing names, you _bind_ them to an object. And each object in the runtime has a dictionary of names-to-object. And even if two objects are of the same class, they can have very different dictionaries. – rodrigo Jan 02 '12 at 14:06
  • In your first example, you created a bona-fide shared resource among all members of the class `MyClass`. You then went on to privatize the contents of a shared trait in each member of the class `MyClass`. – yurisich Jan 02 '12 at 14:07
  • 1
    possible duplicate of [How do I avoid having Python class data shared among instances?](http://stackoverflow.com/questions/1680528/how-do-i-avoid-having-python-class-data-shared-among-instances) – Martijn Pieters Feb 23 '13 at 15:42
  • The same problem occurs in Javascript for slightly different reasons. As a long time javascript programmer I am not surprised now, when I started learning Python. Actually I expected it, that's how I found this post. – Tomáš Zato Sep 07 '15 at 11:43

4 Answers4

55

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)
  1. Is there a list name into the object self?
    • Yes: Use it! Finish.
    • No: Go to 2.
  2. Is there a list name into the class instance of object self?
    • Yes: Use it! Finish
    • No: Error!

But when you bind a name things are different:

self.list = []
  1. Is there a list name into the object self?
    • 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.

c6754
  • 870
  • 1
  • 9
  • 15
rodrigo
  • 94,151
  • 12
  • 143
  • 190
  • ok im following, in that case is there a way to define instance variables outside of the __init__ function, i just tried to do `self.list[]` outside of __init__, this makes sense but is there a way? – jonathan topf Jan 02 '12 at 13:58
  • 1
    @jonathantopf: Instance variables can only be created when you have an instance. They have to be created in a method that's called on an instanced. Your other option is to overload `__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. – Omnifarious Jan 02 '12 at 14:01
  • :) point taken, that makes sense, i hate to ask questions like that but sometimes its the best way to find out if what im thinking is insane or not, now i know! – jonathan topf Jan 02 '12 at 14:03
  • @jonathantopf You can do `p.list = []`, outside any function, in the global scope and it will just work. – rodrigo Jan 02 '12 at 14:04
  • @jonathantopf: Yeah, what you're trying to do is fighting against how Python works and trying to force it into the Java model. Keep in mind that Python basically has no 'declarations'. It's all executable code. The `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. – Omnifarious Jan 02 '12 at 14:09
  • How can I know if an attribute is of an instance or just from its class? That means Python is prototype based, like Javascript? – Chao Aug 23 '17 at 01:28
8

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.

Omnifarious
  • 54,333
  • 19
  • 131
  • 194
  • What devious complications! This answer was really helpful, because the case of 'reading vs. writing' was giving me a headache. – ankush981 Jan 17 '16 at 05:28
4

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']
Wieland
  • 1,663
  • 14
  • 23
0

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).

joaquin
  • 82,968
  • 29
  • 138
  • 152