Metaclasses
Hey guys, we will here learn about metaclasses in python.
The term metaprogramming refers to the potential for a
program to have knowledge of or manipulate itself. Python supports a form of
metaprogramming for classes called metaclasses.
Metaclasses are an esoteric OOP concept, lurking behind
virtually all Python code. You are using them whether you are aware of it or
not. For the most part, you don’t need to be aware of it. Most Python
programmers rarely, if ever, have to think about metaclasses.
“Metaclasses
are deeper magic than 99% of users should ever worry about. If you wonder
whether you need them, you don’t (the people who actually need them know with
certainty that they need them, and don’t need an explanation about why).”
- Tim
Peters
There
are some python programmers who believe you should never use custom
metaclasses. They think that custom metaclasses aren’t necessary.
Still,
understanding Python metaclasses is worthwhile, because it leads to a better
understanding of the internals of Python classes in general. You never know,
you may one day find yourself in one of those situations where you just know
that a custom metaclasses is what you want.
Before
proceeding further we should know about old-style
and new-style classes.
Old-style classes
With
old-style classes, class and type are not quite the same thing.
An instance of an old-style class is always
implemented from a single built-in type called instance. If obj is an instance
of an old-style class, obj.__class__
designates the class, but type (obj)
is always instance.
Let’s
look at the example in python 2.7,
New-style
classes
New-style classes unify the concepts of class
and type. If obj is an instance of a
new-style class, type(obj) is the
same as obj.__class__ (in python3):
Note: - The type of x is class Foo, as you
would expect. But the type of Foo, the class itself, is type. In general, the type of any new-style class is type.
The
type of the built-in classes you are familiar with is also type, i.e. int float, dict, list, tuple
We
know why type() is used for, determining the type of object
used.
Example,
You can also call type() with three arguments—type(<name>, <bases>, <dct>):
- <name> specifies the class name. This becomes the __name__ attribute of the class.
- <bases> specifies a tuple of the base classes from which the class inherits. This becomes the __bases__ attribute of the class.
- <dct> specifies a namespace dictionary containing definitions for the class body. This becomes the __dict__ attribute of the class.
Calling type() in this manner creates a new instance of the type metaclass. In other words, it dynamically creates a new class.
Custom
Metaclasses
Look at the example below,
The
expression Foo() creates a
new instance of class Foo. When the interpreter encounters Foo(), the following occurs:
- The __call__() method of Foo’s
parent class is called. Since Foo is a standard new-style class,
its parent class is the type metaclass, so type’s __call__() method is invoked.
- That __call__() method in turn
invokes the following:
- __new__()
- __init__()
If Foo does
not define __new__() and __init__(), default methods are
inherited from Foo’s ancestry. But if Foo does define these
methods, they override those from the ancestry, which allows for customized behaviour
when instantiating Foo.
In
the following, a custom method called new() is defined and assigned
as the __new__() method for Foo:
This modifies the instantiation behaviour of class Foo: each time an instance of Foo is created, by default it
is initialized with an attribute called attr,
which has a value of 100. (Code like this would more usually appear in the __init__() method and not typically in __new__(). This example is contrived
for demonstration purposes.)