Python Metaclasses
April 28, 2010
When you create a class in python, the mechanism used comes from a class called type
. It is possible to subclass from type
and change the create class behaviour.
This example is taken from the Django model form. The model form automatically creates attributes on itself from the model object it is linked to.
class ModelFormMetaclass(type): def __new__(cls, name, bases, attrs): formfield_callback = attrs.pop('formfield_callback', lambda f: f.formfield()) try: parents = [b for b in bases if issubclass(b, ModelForm)] except NameError: # We are defining ModelForm itself. parents = None declared_fields = get_declared_fields(bases, attrs, False) new_class = super(ModelFormMetaclass, cls).__new__(cls, name, bases, attrs) if not parents: return new_class if 'media' not in attrs: new_class.media = media_property(new_class) opts = new_class._meta = ModelFormOptions(getattr(new_class, 'Meta', None)) if opts.model: # If a model is defined, extract form fields from it. fields = fields_for_model(opts.model, opts.fields, opts.exclude, formfield_callback) # Override default model fields with any custom declared ones # (plus, include all the other declared fields). fields.update(declared_fields) else: fields = declared_fields new_class.declared_fields = declared_fields new_class.base_fields = fields return new_class
This is then used using:
class ModelForm(BaseModelForm): __metaclass__ = ModelFormMetaclass
and this ModelForm is then used for the superclass of specific ModelForms in your application.
The metaclass changes the object creation process. The new function is passed the metaclass itself (conventionally called cls not self), the name of the new class, the superclasses, and the attributes defined. It looks through the classes, adds on the fields from the related model, and then returns the new class.