Python Metaclasses

Python is an object-oriented language that makes working with classes simple and easy. A class in python is a way to describe a specific behaviour for its instances, which are python objects . These objects of that class are created using the class as the blueprint . A metaclass in python is a way to describe a specific behaviour for its instances, which are python classes. A metaclass is the blueprint of the class itself, just like a class is the blueprint for instances of that class. The default metaclass is type, and all metaclasses must derive from type .
Introduction to Python Metaclasses

Class-Factory

A metaclass is mostly used as a class-factory. When you create an object by calling the class, Python creates a new class by calling the metaclass. Combined with the normal __init__ and __new__ methods, metaclasses therefore allow you to do extra things when creating a class, like registering the new class with some registry or replace the class with something else entirely. There are numerous use cases for metaclasses . Here are few:
  1. Automatic property creation
  2. Automatically adding new methods
  3. Registering classes at creation time
  4. Logging and profiling
  5. Interface checking

Metaclass's __new__() and __init__()

  1. __new__(): It's a method which is called before __init__(). It creates the object and return it. We can overide this method to control how the objects are created.

  2. __init__(): This method just initialize the created object passed as parameter.

Metaclasses can be defined in one of the two ways shown below.

class MyMeta1(type): def __new__(cls, name, bases, dict): pass
class MyMeta2(type): def __init__(self, name, bases, dict): pass
To control the creation and initialization of the class in the metaclass , you can implement the metaclass's __new__ method and/or __init__ constructor. Most real-life metaclasses will probably override just one of them. The metaclass behaves just like a class in that it has a __new__ which creates the instance of the class and an __init__ which customizes the instance. Also, you have to use __new__ if you want to return something other than a newly created class of the type in question.

When to use Metaclasses?

In practice, it's rare that someone will have to use metaclasses. Generally speaking, you will have to rely on metaclasses when you need a dynamic behaviour that cannot be described at the object level, but rather at the class level.

A Simple metaclass

We can use type directly to make a class, without any class statement. It can be called in following ways:
  1. When called with only one argument, it returns the type.
  2. When called with three parameters, it creates a class. Following arguments are passed to it:
    1. Class name
    2. Tuple having base classes inherited by class
    3. Class Dictionary
It's because the function type is in fact a metaclass. type is the metaclass Python uses to create all classes behind the scenes.
>>> SomeClass = type('SomeClass',(),{}) >>> SomeClass <class '__main__.SomeClass'>
The class statement isn't just syntactic sugar, it does some extra things, like setting an adequate __qualname__ and __doc__ properties or calling __prepare__ .

The __prepare__ method

This method is called before the class body is executed and it must return a dictionary-like object that's used as the local namespace for all the code from the class body. If the metaclass has a __prepare__ attribute, it is called as namespace = metaclass.__prepare__(name, bases, **kwds). If the metaclass has no __prepare__attribute, then the class namespace is initialized as an empty ordered mapping. It was added in Python 3.0 .
Python metaclasses by example

Creating custom Metaclass

The main purpose of a metaclass is to change the class automatically, when it's created. Normally do this for APIs, where you want to create classes matching the current context. In orderto create your custom metaclass, it have to inherit type metaclass and usually override: custom metaclass:
>>> class MyMeta(type): ... pass

And then we can use it

>>> class DoIT(metaclass=MyMeta): ... pass >>> type(DoIT) <class '__main__.MyMeta'>