Complete Guide to Python Metaclasses – Analytics India Magazine

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 , 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 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(): 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' 

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 .  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 ’s if you want to learn more about Python metaclasses. 


Join Our Telegram Group. Be part of an engaging online community. .

Get the latest updates and relevant offers by sharing your email.