Metaclasses are a handy feature of Python and Django makes good use of them.
When you create certain kinds of classes in Django, a metaclass will do
something to the class before it is created. For forms, the various attributes of the
class are converted into a base_fields
dictionary on the class.
Similarly, a subclass of Model
also fires up a metaclass that does some
registering. A foreignkey to another model adds a relation back on that other
model, for instance.
As a recap, a class is something that can be instantiated into an object. It
can have an __init__()
method that does something upon
instantiation. type(your_instance)
will return the class.
Did you know that you can create classes dynamically? See for yourself:
>>> name = 'ExampleClass'
>>> bases = (object,)
>>> attrs = {'__init__': lambda self: print('Hello from __init__')}
>>> ExampleClass = type(name, bases, attrs)
>>> example = ExampleClass()
Hello From __init__
>>> type(example)
<class '__console__.ExampleClass'>
So… we can actually control how classes are created! You could create a
create_class()
method that calls type but that modifies, for instance, the name.
Or we could take all the attributes and add them to a base_fields
dictionary on the instance. Hey, that’s what we saw in the first Django form example!
Now, what is type
exactly? It is a class that creates classes.
This also means we can subclass it! The most useful thing to override in our
subclass is the __new__()
method. The __init__()
method creates
instances from the class, the __new__()
creates classes. (Update: it
is a little bit different, actually, see Venelin’s comment below). So again we can modify the name and/or the
attributes.
How do you use it in practice? Normally you’d set a __metaclass__
attribute on a class. This tells python to use that metaclass for creating the
class. The same for subclasses. This is how our Django form classes use the
metaclass specified in Django’s base Form
class.
Django uses metaclasses in five places: admin media, models, forms,
formfields, form widgets. Grep for metaclass
in your local django
source code once to get a better feel for how Django uses it.
Note on python 3: it uses a slightly different syntax for specifying metaclasses. So Django 1.5 uses six to support both ways in a single codebase.
Warning: don’t overuse metaclasses. They can make code difficult to debug and follow. Use Django as a good example of how to use metaclasses. Django saves you a lot of work by using metaclasses in a few locations.
See https://github.com/inglesp/Metaclasses
Nice way of giving a presentation, btw. Some sort of semi-interactive python prompt. The software is online at https://github.com/inglesp/prescons
My name is Reinout van Rees and I program in Python, I live in the Netherlands, I cycle recumbent bikes and I have a model railway.
Most of my website content is in my weblog. You can keep up to date by subscribing to the automatic feeds (for instance with Google reader):