What is the difference between class and instance variables in Python?
class Complex:
a = 1
and
class Complex:
def __init__(self):
self.a = 1
Using the call: x = Complex().a in both cases assigns x to 1.
A more in-depth answer about __init__() and self will be appreciated.
When you write a class block, you create class attributes (or class variables). All the names you assign in the class block, including methods you define with
defbecome class attributes.After a class instance is created, anything with a reference to the instance can create instance attributes on it. Inside methods, the "current" instance is almost always bound to the name
self, which is why you are thinking of these as "self variables". Usually in object-oriented design, the code attached to a class is supposed to have control over the attributes of instances of that class, so almost all instance attribute assignment is done inside methods, using the reference to the instance received in theselfparameter of the method.Class attributes are often compared to static variables (or methods) as found in languages like Java, C#, or C++. However, if you want to aim for deeper understanding I would avoid thinking of class attributes as "the same" as static variables. While they are often used for the same purposes, the underlying concept is quite different. More on this in the "advanced" section below the line.
An example!
After executing this block, there is a class
SomeClass, with 3 class attributes:__init__,bar, andbar_list.Then we’ll create an instance:
When this happens,
SomeClass‘s__init__method is executed, receiving the new instance in itsselfparameter. This method creates two instance attributes:fooandfoo_list. Then this instance is assigned into theinstancevariable, so it’s bound to a thing with those two instance attributes:fooandfoo_list.But:
gives:
How did this happen? When we try to retrieve an attribute through the dot syntax, and the attribute doesn’t exist, Python goes through a bunch of steps to try and fulfill your request anyway. The next thing it will try is to look at the class attributes of the class of your instance. In this case, it found an attribute
barinSomeClass, so it returned that.That’s also how method calls work by the way. When you call
mylist.append(5), for example,mylistdoesn’t have an attribute namedappend. But the class ofmylistdoes, and it’s bound to a method object. That method object is returned by themylist.appendbit, and then the(5)bit calls the method with the argument5.The way this is useful is that all instances of
SomeClasswill have access to the samebarattribute. We could create a million instances, but we only need to store that one string in memory, because they can all find it.But you have to be a bit careful. Have a look at the following operations:
What do you think this prints?
This is because each instance has its own copy of
foo_list, so they were appended to separately. But all instances share access to the samebar_list. So when we didsc1.bar_list.append(2)it affectedsc2, even thoughsc2didn’t exist yet! And likewisesc2.bar_list.append(20)affected thebar_listretrieved throughsc1. This is often not what you want.Advanced study follows. 🙂
To really grok Python, coming from traditional statically typed OO-languages like Java and C#, you have to learn to rethink classes a little bit.
In Java, a class isn’t really a thing in its own right. When you write a class you’re more declaring a bunch of things that all instances of that class have in common. At runtime, there’s only instances (and static methods/variables, but those are really just global variables and functions in a namespace associated with a class, nothing to do with OO really). Classes are the way you write down in your source code what the instances will be like at runtime; they only "exist" in your source code, not in the running program.
In Python, a class is nothing special. It’s an object just like anything else. So "class attributes" are in fact exactly the same thing as "instance attributes"; in reality there’s just "attributes". The only reason for drawing a distinction is that we tend to use objects which are classes differently from objects which are not classes. The underlying machinery is all the same. This is why I say it would be a mistake to think of class attributes as static variables from other languages.
But the thing that really makes Python classes different from Java-style classes is that just like any other object each class is an instance of some class!
In Python, most classes are instances of a builtin class called
type. It is this class that controls the common behaviour of classes, and makes all the OO stuff the way it does. The default OO way of having instances of classes that have their own attributes, and have common methods/attributes defined by their class, is just a protocol in Python. You can change most aspects of it if you want. If you’ve ever heard of using a metaclass, all that is is defining a class that is an instance of a different class thantype.The only really "special" thing about classes (aside from all the builtin machinery to make them work they way they do by default), is the class block syntax, to make it easier for you to create instances of
type. This:is roughly equivalent to the following:
And it will arrange for all the contents of
classdictto become attributes of the object that gets created.So then it becomes almost trivial to see that you can access a class attribute by
Class.attributejust as easily asi = Class(); i.attribute. BothiandClassare objects, and objects have attributes. This also makes it easy to understand how you can modify a class after it’s been created; just assign its attributes the same way you would with any other object!In fact, instances have no particular special relationship with the class used to create them. The way Python knows which class to search for attributes that aren’t found in the instance is by the hidden
__class__attribute. Which you can read to find out what class this is an instance of, just as with any other attribute:c = some_instance.__class__. Now you have a variablecbound to a class, even though it probably doesn’t have the same name as the class. You can use this to access class attributes, or even call it to create more instances of it (even though you don’t know what class it is!).And you can even assign to
i.__class__to change what class it is an instance of! If you do this, nothing in particular happens immediately. It’s not earth-shattering. All that it means is that when you look up attributes that don’t exist in the instance, Python will go look at the new contents of__class__. Since that includes most methods, and methods usually expect the instance they’re operating on to be in certain states, this usually results in errors if you do it at random, and it’s very confusing, but it can be done. If you’re very careful, the thing you store in__class__doesn’t even have to be a class object; all Python’s going to do with it is look up attributes under certain circumstances, so all you need is an object that has the right kind of attributes (some caveats aside where Python does get picky about things being classes or instances of a particular class).That’s probably enough for now. Hopefully (if you’ve even read this far) I haven’t confused you too much. Python is neat when you learn how it works. 🙂