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. 

Download our Mobile App

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.

Subscribe to our newsletter

Join our editors every weekday evening as they steer you through the most significant news of the day.
Your newsletter subscriptions are subject to AIM Privacy Policy and Terms and Conditions.

Our Recent Stories

Our Upcoming Events

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
MOST POPULAR
How Generative AI is Revolutionising Data Science Tools

How Generative AI is Revolutionising Data Science Tools

Einblick Prompt enables users to create complete data workflows using natural language, accelerating various stages of data science and analytics. Einblick has effectively combined the capabilities of a Jupyter notebook with the user-friendliness of ChatGPT.

Intel Goes All in on AI

Pat Gelsinger said, there are three types of chip manufacturers, “you’re big, you’re niche or you’re dead”