Metaclasses in Python

Introduction

In Python, metaclasses define how classes themselves behave. While regular classes define the structure and behavior of objects, metaclasses define the structure and behavior of classes.

In this tutorial, we will cover:

  1. What is a metaclass?
  2. The role of type in class creation
  3. Creating custom metaclasses
  4. Practical use cases of metaclasses

1. What is a Metaclass?

  • A metaclass is a class that creates classes.
  • It controls the creation, modification, and behavior of classes.
  • By default, Python uses type as the metaclass for all classes.

 Analogy:

  • Objects are instances of a class → A dog is an instance of the Animal class.
  • Classes are instances of a metaclassAnimal is an instance of the type metaclass.
class MyClass:
    pass

print(type(MyClass))  # Output: <class 'type'>

2. The Role of type in Class Creation

The type function in Python is both:

  • A constructor that creates new types (metaclass functionality).
  • A function that returns the type of an object.

Example: Creating a class dynamically using type

# Creating a class dynamically using type()
Student = type('Student', (), {'school': 'Delhi Public School'})

# Creating an instance
s1 = Student()
print(s1.school)  # Output: Delhi Public School

Breaking down type('Student', (), {'school': 'DPS'})

  • 'Student' → Name of the class
  • () → Tuple of parent classes (empty means no inheritance)
  • {'school': 'DPS'} → Class attributes

3. Creating Custom Metaclasses

A custom metaclass allows us to modify class creation before the class is instantiated.

class CustomMeta(type):
    def __new__(cls, name, bases, class_dict):
        print(f"Creating class: {name}")
        class_dict['country'] = 'India'  # Adding a default attribute
        return super().__new__(cls, name, bases, class_dict)

# Using the metaclass
class Student(metaclass=CustomMeta):
    def __init__(self, name):
        self.name = name

# Creating an object
s1 = Student("Amit")
print(s1.country)  # Output: India

How it works?

  • The __new__ method is called before the class is created.
  • The class dictionary (class_dict) is modified to include country = 'India'.
  • The modified class is returned and used as Student.

4. Practical Use Cases of Metaclasses

Enforcing Coding Standards

You can use metaclasses to ensure all class attributes are uppercase.

class UpperCaseMeta(type):
    def __new__(cls, name, bases, class_dict):
        for attr_name in class_dict:
            if not attr_name.isupper() and not attr_name.startswith('__'):
                raise TypeError(f"Attribute '{attr_name}' must be uppercase")
        return super().__new__(cls, name, bases, class_dict)

class Config(metaclass=UpperCaseMeta):
    API_KEY = "12345"
    DEBUG_MODE = True  # This will raise an error!

# Output: TypeError: Attribute 'DEBUG_MODE' must be uppercase

Singleton Pattern Using Metaclasses

A singleton ensures that only one instance of a class exists.

class SingletonMeta(type):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]

class Database(metaclass=SingletonMeta):
    pass

db1 = Database()
db2 = Database()
print(db1 is db2)  # Output: True (Same instance)

5. Summary

Metaclasses define how classes are created
type is the default metaclass in Python
Custom metaclasses allow modifying class behavior before instantiation
Use cases include enforcing coding standards, singletons, and dynamic modifications