Let's start with a brief introduction to metaclasses. A metaclass is a class of a class, which means that the class is an instance of its metaclass. With metaclasses, programmers get an opportunity to create classes of their own type from the predefined Python classes. For instance, if you have an object, MyClass
, you can create a metaclass, MyKls
, that redefines the behavior of MyClass
to the way that you need. Let's understand them in detail.
In Python, everything is an object. If we say a=5
, then type(a)
returns <type 'int'>
, which means a
is of the int type. However, type(int)
returns <type 'type'>
, which suggests the presence of a metaclass as int is a class of the type
type.
The definition of class is decided by its metaclass, so when we create a class with class A
, Python creates it by A = type(name, bases, dict)
:
name
: This is the name of the classbase
: This is the base classdict
: This is the attribute variableNow, if a class has a predefined metaclass (by the name of MetaKls
), Python creates the class by A = MetaKls(name, bases, dict)
.
Let's look at a sample metaclass implementation in Python 3.5:
class MyInt(type): def __call__(cls, *args, **kwds): print("***** Here's My int *****", args) print("Now do whatever you want with these objects...") return type.__call__(cls, *args, **kwds) class int(metaclass=MyInt): def __init__(self, x, y): self.x = x self.y = y i = int(4,5)
The following is the output of the preceding code:
Python's special __call__
method gets called when an object needs to be created for an already existing class. In this code, when we instantiate the int
class with int(4,5)
, the __call__
method of the MyInt
metaclass gets called, which means that the metaclass now controls the instantiation of the object. Wow, isn't this great?!
The preceding philosophy is used in the Singleton design pattern as well. As the metaclass has more control over class creation and object instantiation, it can be used to create Singletons. (Note: To control the creation and initialization of a class, metaclasses override the __new__
and __init__
method.)
The Singleton implementation with metclasses can be explained better with the following example code:
class MetaSingleton(type): _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = super(MetaSingleton, cls).__call__(*args, **kwargs) return cls._instances[cls] class Logger(metaclass=MetaSingleton): pass logger1 = Logger() logger2 = Logger() print(logger1, logger2)
18.223.33.157