Metaclasses are one of Python's most powerful yet least understood features. They enable patterns that would be difficult or impossible with regular class definitions. In this post, we'll explore metaclass fundamentals through simple examples.
What are Metaclasses?
A class C's metaclass is basically type(C). If you define class Class(metaclass=ClassMeta) in Python, then type(Class) is ClassMeta.
If we don't explicitly set a metaclass for a class, then its metaclass is type by default. Thus, ClassMeta should inherit from type.
Just like given o = Class(...), and that Class defines a method def f(self, ...), o.f(...) would result in calling Class.f(o, ...), if ClassMeta defines a method def g(self, ...), Class.g(...) would result in calling ClassMeta.g(...).
Class(0, 1, 2, message='Hello World') is syntactic sugar for Class.__call__(0, 1, 2, message='Hello World').
If we haven't set a metaclass for Class, then this in turn invokes type.__call__(Class, 0, 1, 2, message='Hello World').
However, we have set Class's metaclass to ClassMeta, whose ClassMeta.__call__ overrides type.__call__. Thus, Class.__call__(0, 1, 2, message='Hello World') would invoke ClassMeta.__call__(Class, 0, 1, 2, message='Hello World') instead.
With a few print statements added, we can see the function calls:
classClassMeta(type): _values_to_instantiations = {} value = None
def__getitem__(self, value): if value in self._values_to_instantiations: instantiation = self._values_to_instantiations[value] else: # Dynamically create `self[value]` containing the class variable VALUE as a subclass of `self` instantiation = type( '%s[%s]' % (self.__name__, value), (self,), { 'VALUE': value } ) self._values_to_instantiations[value] = instantiation
return instantiation
classClass(object, metaclass=ClassMeta): # Use `self.VALUE` or `cls.VALUE` here pass
Then, after we run:
1 2 3 4 5 6 7 8
# Dynamically create `Class[True]` class_true = Class[True] # Create an instance of `Class[True]` c1 = class_true() # Dynamically create `Class[False]` class_false = Class[False] # Create an instance of `Class[False]` c2 = class_false()
we can get something like:
1 2 3 4
>>>c1 >>><__main__.Class[True] at 0x796bbb89d3a0> >>>c2 >>><__main__.Class[False] at 0x796bbc53fb60>