8

Beginner question here! Some time ago, I asked this question: Parse CSV records into a list of Classes, which was also answered more technically here: How do I avoid having class data shared among instances?

I learned that, in Python classes, variables that will be defined on a per-object basis need to be declared in the __init__(self) function.

So for:

class ClassOne:
    def __init__(self, datetime):
        self.datetime = datetime
    v = []

the variable v will hold the same data for all instances of ClassOne, whereas for:

class ClassTwo:
    def __init__(self, datetime):
        self.datetime = datetime
        self.v = []

variable v holds individual data for each instance of ClassTwo.

However, in Django (which I'm learning now), I see the "normal" (more C++ like) behavior again for the variables:

class Post(models.Model):
    title = models.CharField(max_length = 255)

Here, the variable title holds individual data for each instance of Post, despite not being defined in the __init__ function.

My basic question is Why or How does title pertain to individual class objects instead of being common to every class object, as v in ClassOne is above?

If I'm understanding this right, this means that Django classes are interpreted differently than normal Python classes? However, that conclusion doesn't make sense...

I hope that someone can help me understand this. It was my assumption previously that python code (say, a data analysis or a scientific model) could be built into a web-based service by using it's classes and routines in a Django app. If the implementation of the two different classes is different, then this would be quite difficult!

This may have been answered elsewhere. I'm not well versed in Django jango, so don't know what to search for.

Community
  • 1
  • 1
Swift Arrow
  • 401
  • 4
  • 9
  • You probably meant to set `self.v = []` in `ClassTwo.__init__`; just `v` is a function local. – Martijn Pieters Jan 01 '14 at 15:26
  • You're right on that, I fixed it. I also clarified the question a little: why is `title` treated differently than `v`? – Swift Arrow Jan 01 '14 at 15:46
  • What makes you think it is treated differently? It is no different from setting any other attribute on the class; you can refer directly to those attributes **on the class**. `Post.title` now exists. You can enumerate all attributes on the `Post` class and the list will include `title`. That is the *point*. – Martijn Pieters Jan 01 '14 at 15:46
  • 1
    And the `title` attribute **is** shared among instances as well, but the instances have a `title` attribute **as well**. – Martijn Pieters Jan 01 '14 at 15:48

4 Answers4

7

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.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • So is it right to understand that the `title` attribute is a class instance _inside_ the `Post` class? Similar to, say, `Toppings` (a class with variables such as `type`, `amount`, etc.), which is used in the `Pizza` class? Per my understanding, `Toppings` (and `title`, for that matter) would still need to be declared inside the `__init__` function, no? – Swift Arrow Jan 01 '14 at 15:33
  • No, you don't **have** to. You can use class attributes to *deliberately* share data, or to provide defaults. Once you set an attribute on the instance (`self.v = []` is setting such an attribute), the attribute on the class with the same name is masked. So you can set defaults on the class, and override specific attributes on instances. – Martijn Pieters Jan 01 '14 at 15:35
  • I'm still not clear on why `title` is _not_ a class attribute? – Swift Arrow Jan 01 '14 at 15:49
  • 2
    @SwiftArrow: It is **both** a class attribute and an instance attribute. There are two separate attributes, and Python finds one during lookup. – Martijn Pieters Jan 01 '14 at 15:50
4

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.

  • Thanks, this seems to have made things a little clearer... So if I understand you correctly, Django's `models` process their inheriting classes and auto-create an `__init__` function, putting the actual data containers in it, for each class? Is it safe to say that if I wanted to make a class attribute, I could add a simple data variable in the django class definition? – Swift Arrow Jan 01 '14 at 15:54
  • 1
    @SwiftArrow: The base class provides the `__init__` method for you. There is no need to generate it. You can look at the [model base class on GitHub](https://github.com/django/django/blob/master/django/db/models/base.py#L365) if you so desire. – Martijn Pieters Jan 01 '14 at 15:57
4

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.

Kevin S.
  • 628
  • 6
  • 9
0

This is a really old thread. I happen to get the same question while I'm just new to Django. Class of 'title' is models.CharField, which seems a python descriptor. According to the Descriptor definition, 'title' is a class variable. p.title = 'xxx', there is no instance 'title'. The above statement calls the class variable title to create a hidden title for the instance. print(p.title) will just call the class variable title to return the invisible title of instance.

https://docs.python.org/3.7/howto/descriptor.html Python cookbook chapter 8.6 also talks about the Descriptor.

Hope I'm correct and could help.