MITB Banner

Complete Guide to Python Metaclasses

Python metaclasses is an abstruse OOP concept that lurks behind basically all Python code, it can be used to modify the behaviour of classes.
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.

 a = 42
 print(a.__class__)
 print(a.__class__.__class__)
 <type 'int'>
 <type 'type'>

 def func(): pass
 print(func.__class__)
 print(func.__class__.__class__)
 <type 'function'>
 <type 'type'>

 class XYZ(object): pass
 x = XYZ()
 print(x.__class__)
 print(x.__class__.__class__)
 <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.")

 Dummy.meme()
 When you try to define constants 
 Python: We don’t do that here. 

The above syntax is equivalent to:

 def nameless_func():
     print("C++ – Can’t compare 'float' and 'int'. \nPython – Variable is variable.")
 WierdDummy = type('WierdDummy',() ,{'x':12, 'meme': nameless_func})
 
 WierdDummy.meme()
 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()
 obj.show_a()
 obj.show_b()
 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):
         print("Expelliarmus!")

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()
 obj.magic()
 Expelliarmus!

 print(obj.X)
 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. 

Access all our open Survey & Awards Nomination forms in one place >>

Picture of Aditya Singh

Aditya Singh

A machine learning enthusiast with a knack for finding patterns. In my free time, I like to delve into the world of non-fiction books and video essays.

Download our Mobile App

CORPORATE TRAINING PROGRAMS ON GENERATIVE AI

Generative AI Skilling for Enterprises

Our customized corporate training program on Generative AI provides a unique opportunity to empower, retain, and advance your talent.

3 Ways to Join our Community

Telegram group

Discover special offers, top stories, upcoming events, and more.

Discord Server

Stay Connected with a larger ecosystem of data science and ML Professionals

Subscribe to our Daily newsletter

Get our daily awesome stories & videos in your inbox
Recent Stories