Difference between Class Members and Instance Members in Django and "Ordinary" Python?
The title
attribute is not data. It holds a model description only; an object describing what type of information the title
field should hold.
As such it is part of the class definition; individual instances of the Post
class will have a title
attribute that conforms to the constraints set in the models.CharField()
instance on the class.
You need to build such a model to describe to Django how to build form fields and how to build a SQL table for the Post
instances; both are concepts that need to have more type information than what Python normally itself needs.
Individual instances of Post
are given a title
attribute as well. That attribute then masks the class attribute:
p = Post(title='Some title')
print p.title # prints 'Some title'
Python looks at the instance directly first; if it does not have a title
attribute, lookup would then move to the class object. But that's not needed here, the Post.title
attribute is not found as the instance has a title
attribute itself.
In Python itself, there is no absolute distinction between 'data' and methods, by the way. Everything in Python is an object, including classes and methods. As such, looking up an attribute on an instance can find an object there too, including methods. If an attribute lookup there fails, then Python will look for the attribute on the class and base classes, and if that fails, lookup falls back to the metaclass even.
This is where mutable attributes come in; looking up ClassOne().v
fails on the instance, but succeeds on the class. Manipulating that list then alters ClassOne.v
the class attribute, and looking up v
on other instances once again will find the class attribute. This is how class attributes are shared, just like the methods on the class.
Django does not change the rules of the language. It does however use the language creatively. Just like class ClassTwo(...): v = []
creates one list and stores it in the class, class Post(...): title = something
creates one something
and stores it in the class. In this case, said something is not a char field value like "foo"
, it's an object which represents the concept of a char field with a max_length of 255.
Django gathers these objects representing database types, and creates (among many other things) an __init__
method that gives Post
instances an attribute of the same name (which does contain an actual string value). The implementation of this is quite advanced, but firmly within the rules of the Python language - you and I can create our own Python libraries doing something similar. Anyway, since instance attributes shadow class attributes, you never notice that Post.title
exists only once and isn't actually a title string. a_post_object.title
always gives you the instance attribute.
As a slightly more general explanation of the relationship between class and instance variables, consider the following example that is unrelated to django models:
>>> class A(object):
... x = 2
... y = 1
...
... def __init__(self):
... self.x = 3
...
>>> A.x
2
>>> instance = A()
>>> instance.x
3
>>> instance.y
1
>>> instance.y = 4
>>> instance.y
4
>>> A.y
1
There are 2 things that I think are worth noting here. Firstly, a separate class and instance variable of the same name can exist. The class variable is only accessible directly from an instance if there is no instance variable of the same name. This is how the django models work, the class variables are fields (which are descriptions of the instance variables), the instance variables are the values for the specific instances. Using the same name for class and instance variables can be confusing, and isn't something to be done lightly. In the case of django models I think it works really well, but still can cause some headaches (I had similar questions when I first used django).
The second thing to note is that you can assign variables to an instance anywhere, it doesn't have to be in the __init__
function or even in a method of the instance's class, it can be anywhere. That is not to say that making a point of defining all instance variables in the __init__
function is a bad idea. In many cases it is a good idea.