In Python, every object has a type, and the type of an object is an instance of type.
>>> type(0)
<type 'int'>
>>> isinstance(type(0), type)
True
>>> class A(object): pass
>>> type(A)
<type 'type'>
>>> a = A()
>>> type(a)
<class 'A'>
Even type has a type which is an instance of type (although it’s a little silly).
>>> type(type)
<type 'type'>
>>> isinstance(type, type)
True
The metaclass of an object is defined to be the type of its type.
>>> def metaclass(obj):
... return type(type(obj))
>>> metaclass(0)
<type 'type'>
>>> metaclass(metaclass)
<type 'type'>
It’s quite hard to create an object whose metaclass is not type.
In Python anything that is a type can be subclassed. So we can subclass type itself.
>>> class subtype(type): pass
We can now use subtype in pretty much the same way as type itself. In particular we can use it to construct an empty class.
>>> cls = subtype('name', (object,), {})
Let’s look at the type and metaclass of cls.
>>> type(cls), metaclass(cls)
(<class 'subtype'>, <type 'type'>)
Notice that type(cls) is not type. This is our way in. Here’s an instance of cls, followed by its type and metaclass.
>>> obj = cls()
>>> type(obj), metaclass(obj)
(<class 'name'>, <class 'subtype'>)
We have just constructed an object with a non-trivial metaclass. The metaclass of obj is subtype.
When Python executes
obj[key]
behind the scenes it executes
obj.__getitem__[key]
Here’s an example:
>>> class A(object):
... def __getitem__(self, key):
... return getattr(self, key)
>>> obj = A()
>>> obj['name']
Traceback (most recent call last):
AttributeError: 'A' object has no attribute 'name'
>>> obj.name = 'some value'
>>> obj['name']
'some value'
There are two main reasons for introducing and using a metaclass, or in other words a subclass of type.