One is a class attribute, while the other is an instance attribute. They are different, but they are closely related to one another in ways that make them look the same at times.
It has to do with the way python looks up attributes. There's a hierarchy. In simple cases it might look like this:
instance -> Subclass -> Superclass -> object (built-in type)
When you look for an attribute on instance
like this...
`instance.val`
...what actually happens is that first, Python looks for val
in the instance itself. Then, if it doesn't find val
, it looks in its class, Subclass
. Then, if it doesn't find val
there, it looks in the parent of Subclass
, Superclass
. This means that when you do this...
>>> class Foo():
foovar = 10
def __init__(self, val):
self.selfvar = val
...all instances of Foo
share foovar
, but have their own distinct selfvar
s. Here's a simple, concrete example of how that works:
>>> f = Foo(5)
>>> f.foovar
10
>>> Foo.foovar
10
If we don't touch foovar
, it's the same for both f
and Foo
. But if we change f.foovar
...
>>> f.foovar = 5
>>> f.foovar
5
>>> Foo.foovar
10
...we add an instance attribute that effectively masks the value of Foo.foovar
. Now if we change Foo.foovar
directly, it doesn't affect our foo
instance:
>>> Foo.foovar = 7
>>> f.foovar
5
But it does affect a new foo
instance:
>>> Foo(5).foovar
7
Also keep in mind that mutable objects add another layer of indirection (as mgilson reminded me). Here, f.foovar
refers to the same object as Foo.foovar
, so when you alter the object, the changes are propagated up the hierarchy:
>>> Foo.foovar = [1]
>>> f = Foo(5)
>>> f.foovar[0] = 99
>>> Foo.foovar
[99]