Now Reading
Complete Guide to Python Metaclasses

Complete Guide to Python Metaclasses

python metaclasses

Metaprogramming refers to a programming technique that enables you to write code that can manipulate code. We’ve already discussed one form of metaprogramming supported by Python: decorators; this article will cover Python metaclasses. Before understanding metaclasses, you need to master classes in Python. I recommend reading these articles before continuing  –

Borrowed from Smalltalk, Python has an odd idea of classes. Generally, classes are pieces of code that can be used to create objects. This is true for Python too, however, in Python, classes are objects too. Yes, objects. Everything in Python is an object, even classes.

Register for FREE Workshop on Data Engineering>>
 a = 42
 <type 'int'>
 <type 'type'>

 def func(): pass
 <type 'function'>
 <type 'type'>

 class XYZ(object): pass
 x = XYZ()
 <class '__main__.XYZ'>
 <type 'type'> 

Python Metaclasses

Every object and class in Python is either an instance of a class or an instance of a metaclass. Every class inherits from the built-in basic base class object, and every class is an instance of the metaclass type. Except for type, type is its metaclass and base class (don’t ask “how?”, it’s done using an implementation level hack). Just like how a class defines the behaviour of its object, a metaclass defines the behaviour of classes. The main purpose of metaclasses is to change the behaviour of classes as soon as they are created. 

Although you’ve probably never explicitly used metaclasses, they’re littered everywhere if you were to look under the hood. For instance, if you’ve ever created an abstract class in Python using the ABC module, you indirectly inherited the ABCmeta class. Or, if you’re a backend developer who uses Django, you’ve indirectly used the ModelBase metaclass through model.Model

Much like how you can dynamically create objects of a class using the syntax: class_name(), you create a class using the syntax: type(). Let’s illustrate this with an example:

 class Dummy():
     x = 12
     def meme():
         print("When you try to define constants \nPython: We don’t do that here.")
 When you try to define constants 
 Python: We don’t do that here. 

The above syntax is equivalent to:

See Also

 def nameless_func():
     print("C++ – Can’t compare 'float' and 'int'. \nPython – Variable is variable.")
 WierdDummy = type('WierdDummy',() ,{'x':12, 'meme': nameless_func})
 C++ – Can’t compare 'float' and 'int'. 
 Python – Variable is variable. 

Here, WierdDummy is the new class’s name, () is a tuple containing the base class(es) that can be empty.  {'x':12, 'meme': nameless_func} is a dictionary that stores all class attribute names and values. At first glance, this syntax seems obscure and useless, and it mostly is, but it can be extremely powerful for niche metaprogramming use cases. Imagine this scenario: You have four unrelated mixin classes with different functionalities, and you need to create all possible combinations of two. Now you could write all 6 new classes manually or dynamically create them with a few lines of code.

 class A:
     def show_a(self):
         print("Class A")

 class B:
     def show_b(self):
         print("Class B")

 class C:
     def show_c(self):
         print("Class C")

 class D:
     def show_d(self):
         print("Class D")

 from itertools import combinations
 for base_classes in combinations([A, B, C, D], 2):
     new_class_name = "".join([c.__name__ for c in base_classes])
     globals()[new_class_name] = type(new_class_name , base_classes,{})

 obj = AB()
 Class A
 Class B 

Creating Metaclasses in Python

To create your own custom metaclasses in Python, you need to inherit type, and to inherit from a custom metaclass; you need to explicitly specify it using metaclass=. Let’s create a metaclass for enforcing the PEP8 naming convention for functions and variables. 

 from warnings import warn
 class EnforcePEP(type):
     def __new__(cls, clsname, bases, clsdct):
         new_dict = {}
         for attr, val in clsdct.items():
             if attr.lower() != attr:
                 warn(f"Function/Variable naming convention not followed! '{attr}' will now be '{attr.lower()}'")
             new_dict[attr.lower()] = val
         return type(clsname, bases, new_dict)

 class Example(metaclass = EnforcePEP):
     X = 12
     nAme = "Dummy Class"
     def MAgic(self):

Warning (from warnings module):
   File "<pyshell#3>", line 6
UserWarning: Function/Variable naming convention not followed! 'X' will now be 'x'
Warning (from warnings module):
   File "<pyshell#3>", line 6
UserWarning: Function/Variable naming convention not followed! 'nAme' will now be 'name'
Warning (from warnings module):
   File "<pyshell#3>", line 6
UserWarning: Function/Variable naming convention not followed! 'MAgic' will now be 'magic' 
 obj = Example()

 AttributeError: 'Example' object has no attribute 'X' 
The relationship of the python metaclass type with custom metaclasses, classes and objects.

Last Epoch

This article discussed Python metaclasses, an abstruse OOP concept that lurks behind basically all Python code. The chances of you needing to use or create metaclasses are extremely low unless you’re creating a library of your own or complex APIs. And even then, most of your class augmentation needs can be satisfied by using decorators or simply monkey patching.  That being said, not all object-oriented programming languages support metaclasses. It’s good to know that if the need arises Python provides the capability to define custom metaclasses. I highly recommend Mark Smith’s talk at PyCon AU 2019 if you want to learn more about Python metaclasses. 

Subscribe to our Newsletter

Get the latest updates and relevant offers by sharing your email.
Join our Telegram Group. Be part of an engaging community

Copyright Analytics India Magazine Pvt Ltd

Scroll To Top