Classes are used to make objects. However, a class itself is an object and, just like any other object, it’s an instance of something: a metaclass.
A metaclass is the class of a class; it defines how a class behaves.
Hence, if we want to modify the behavior of classes, we will need to write our own custom metaclass.
The default metaclass is type
.
This means that just like an object is of the type class
, a class itself is of the type, type
.
class Foobar:passfoo = Foobar()print(type(foo))print(type(Foobar))
Running the code extract above shows us that since foo
is an instantiation of the class Foobar
, its type is the class Foobar
. The class Foobar
, on the other hand, displays its own type as the class type
, which, as we earlier said, is the default metaclass.
The type()
function can be used to directly define classes by passing it through:
To create a custom metaclass, we first have to inherit the default metaclass, type
, and then override its __new__()
and __init__()
methods. The following code defines a metaclass with a custom __new__()
method which defines a new attribute x
in the class. The Foo
class inherits the attributes of its metaclass Meta:
class Meta(type):# overriding the new method of the metaclassdef __new__(cls, name, bases, dct):x = super().__new__(cls, name, bases, dct)# defining that each class with th=is metaclass# should have a variable, x with a default valuex.attr = 100return x# defining a class with Meta as its metaclass instead# of typeclass Foo(metaclass = Meta):pass# printing the variables in our newly defined classprint(Foo.attr)
Metaclasses can be applied in logging, registration of classes at creation time, profiling, and others. They propagate down the inheritance hierarchies, allowing all subclasses to inherit their attributes and methods. This property is particularly useful if we want our classes to have specific attributes, automatically, at the time of their creation.