In Python, everything is an object, including classes themselves. Normally, we create classes using the class
keyword, but behind the scenes, Python uses metaclasses to create these classes.
Metaclasses define how classes behave, similar to how classes define how objects behave.
They are “classes of classes”—meaning they control the creation and behavior of other classes.
Key Concepts:
- A class creates objects.
- A metaclass creates classes.
1. Understanding Metaclasses
1.1 The type
Function – The Default Metaclass
In Python, type
is the built-in metaclass used by default to create classes dynamically.
Example: Creating a Class Dynamically Using type
# Creating a class dynamically
MyClass = type('MyClass', (), {'x': 10, 'greet': lambda self: "Hello"})
# Creating an instance
obj = MyClass()
print(obj.x) # Output: 10
print(obj.greet()) # Output: Hello
How it works?
'MyClass'
→ The name of the class.()
→ Base classes (empty means no inheritance).{}
→ Class attributes and methods.
2. Custom Metaclasses
We can create custom metaclasses by inheriting from type
and overriding its methods.
2.1 The __new__
and __init__
Methods
__new__(cls, name, bases, dct)
: Creates and returns a new class.__init__(cls, name, bases, dct)
: Initializes the class after creation.
Example: Custom Metaclass
class Meta(type): # Inheriting from 'type'
def __new__(cls, name, bases, dct):
print(f"Creating class {name}")
dct['greeting'] = "Hello from Metaclass"
return super().__new__(cls, name, bases, dct)
# Using the metaclass
class MyClass(metaclass=Meta):
pass
print(MyClass.greeting) # Output: Hello from Metaclass
What happens here?
Meta.__new__()
is called beforeMyClass
is created.- A new attribute
greeting
is added dynamically. super().__new__()
returns the new class.
3. Controlling Class Creation
3.1 Restricting Class Attributes
We can enforce rules on classes using metaclasses.
Example: Preventing Attributes Not in allowed_attributes
class AttributeControlMeta(type):
def __new__(cls, name, bases, dct):
allowed_attributes = {"name", "age"}
for attr in dct:
if attr not in allowed_attributes and not attr.startswith('__'):
raise AttributeError(f"'{attr}' is not allowed in {name}")
return super().__new__(cls, name, bases, dct)
class Person(metaclass=AttributeControlMeta):
name = "John"
age = 30
city = "New York" # This will cause an error
Why use this?
- Ensures class attribute constraints are enforced.
3.2 Automatically Adding Methods
Metaclasses can inject methods into classes dynamically.
Example: Adding a describe()
Method Automatically
class AutoMethodMeta(type):
def __new__(cls, name, bases, dct):
dct['describe'] = lambda self: f"This is an instance of {name}"
return super().__new__(cls, name, bases, dct)
class Animal(metaclass=AutoMethodMeta):
pass
a = Animal()
print(a.describe()) # Output: This is an instance of Animal
Why use this?
- Automatically adds a common behavior to all classes using the metaclass.
4. Practical Use Cases for Metaclasses
1. Enforcing Coding Standards
Metaclasses can enforce naming conventions or ensure required methods exist.
Example: Enforcing Method Naming Convention (snake_case
)
class NamingConventionMeta(type):
def __new__(cls, name, bases, dct):
for attr in dct:
if callable(dct[attr]) and not attr.islower():
raise TypeError(f"Method '{attr}' in class '{name}' must be in snake_case")
return super().__new__(cls, name, bases, dct)
class MyClass(metaclass=NamingConventionMeta):
def valid_method(self): # OK
pass
def InvalidMethod(self): # This will cause an error
pass
2. Plugin and Framework Development
Metaclasses can register classes automatically in frameworks.
Example: Auto-Registering Classes
registry = {}
class AutoRegisterMeta(type):
def __new__(cls, name, bases, dct):
new_cls = super().__new__(cls, name, bases, dct)
registry[name] = new_cls # Auto-register class
return new_cls
class Plugin(metaclass=AutoRegisterMeta):
pass
class MyPlugin(Plugin):
pass
print(registry) # Output: {'Plugin': <class '__main__.Plugin'>, 'MyPlugin': <class '__main__.MyPlugin'>}
Why use this?
- Automatically keeps track of all plugin subclasses.
5. When to Use Metaclasses?
Use metaclasses when:
- You need class-level validation (e.g., enforcing attributes, naming rules).
- You want to automatically add methods or properties to classes.
- You are building frameworks or plugins where auto-registration is needed.
- You need deep customization of class behavior.
Avoid metaclasses if:
- Simple class inheritance can achieve the same goal.
- The problem can be solved with decorators or mixins (metaclasses add complexity).
6. Summary Table
Feature | Description |
---|---|
What is a Metaclass? | A “class of classes” that defines how classes behave. |
Default Metaclass | type is the built-in metaclass in Python. |
Creating a Metaclass | Inherit from type and override __new__() or __init__() . |
Common Uses | Enforcing rules, adding methods, registering classes, frameworks. |
Alternatives | Decorators, Mixins, Class Factories (if metaclasses are too complex). |