In Python, everything is an object—including classes themselves! But who creates classes?
The answer: Metaclasses.
Metaclasses define how classes behave. They allow you to:
Control class creation dynamically
Modify class attributes and methods before instantiation
Enforce coding standards or restrictions
Prevent inheritance using metaclasses
In this post, we’ll explore:
️ What metaclasses are
️ How Python classes are created
️ How to define custom metaclasses
️ Preventing inheritance using metaclasses
️ Real-world applications of metaclasses
1️⃣ Understanding Metaclasses: What Are They?
In Python, a class is an instance of a metaclass. Just as objects are created from classes, classes themselves are created from metaclasses.
Example: Normal Class-Object Relationship
<span>class</span> <span>Dog</span><span>:</span> <span># Dog is a class </span> <span>def</span> <span>speak</span><span>(</span><span>self</span><span>):</span><span>return</span> <span>"</span><span>Woof!</span><span>"</span><span>d</span> <span>=</span> <span>Dog</span><span>()</span> <span># d is an object (instance of Dog) </span><span>print</span><span>(</span><span>d</span><span>.</span><span>speak</span><span>())</span> <span># Woof! </span><span>class</span> <span>Dog</span><span>:</span> <span># Dog is a class </span> <span>def</span> <span>speak</span><span>(</span><span>self</span><span>):</span> <span>return</span> <span>"</span><span>Woof!</span><span>"</span> <span>d</span> <span>=</span> <span>Dog</span><span>()</span> <span># d is an object (instance of Dog) </span><span>print</span><span>(</span><span>d</span><span>.</span><span>speak</span><span>())</span> <span># Woof! </span>class Dog: # Dog is a class def speak(self): return "Woof!" d = Dog() # d is an object (instance of Dog) print(d.speak()) # Woof!
Enter fullscreen mode Exit fullscreen mode
Now, let’s check the class of Dog itself!
print<span>(</span><span>type</span><span>(</span>Dog<span>))</span> <span># <class 'type'></span>print<span>(</span><span>type</span><span>(</span>Dog<span>))</span> <span># <class 'type'></span>print(type(Dog)) # <class 'type'>
Enter fullscreen mode Exit fullscreen mode
Observation:
d is an instance of Dog.Dog itself is an instance of type.d is an instance of Dog. Dog itself is an instance of type.d is an instance of Dog. Dog itself is an instance of type.
Enter fullscreen mode Exit fullscreen mode
This means type is a metaclass—it is the class of all classes in Python!
2️⃣ Creating Classes Dynamically Using type
Python’s built-in metaclass is type, which allows us to create classes dynamically.
Creating a class manually using type
<span># Equivalent to: class Animal: pass </span><span>Animal</span> <span>=</span> <span>type</span><span>(</span><span>'</span><span>Animal</span><span>'</span><span>,</span> <span>(),</span> <span>{})</span><span>a</span> <span>=</span> <span>Animal</span><span>()</span><span>print</span><span>(</span><span>type</span><span>(</span><span>a</span><span>))</span> <span># <class '__main__.Animal'> </span><span>print</span><span>(</span><span>type</span><span>(</span><span>Animal</span><span>))</span> <span># <class 'type'> </span><span># Equivalent to: class Animal: pass </span><span>Animal</span> <span>=</span> <span>type</span><span>(</span><span>'</span><span>Animal</span><span>'</span><span>,</span> <span>(),</span> <span>{})</span> <span>a</span> <span>=</span> <span>Animal</span><span>()</span> <span>print</span><span>(</span><span>type</span><span>(</span><span>a</span><span>))</span> <span># <class '__main__.Animal'> </span><span>print</span><span>(</span><span>type</span><span>(</span><span>Animal</span><span>))</span> <span># <class 'type'> </span># Equivalent to: class Animal: pass Animal = type('Animal', (), {}) a = Animal() print(type(a)) # <class '__main__.Animal'> print(type(Animal)) # <class 'type'>
Enter fullscreen mode Exit fullscreen mode
What’s happening?
type(name, bases, attrs) creates a new class dynamically.name: The class name ('Animal').bases: Parent classes (empty () means no inheritance).attrs: Attributes and methods ({} means no attributes).type(name, bases, attrs) creates a new class dynamically. name: The class name ('Animal'). bases: Parent classes (empty () means no inheritance). attrs: Attributes and methods ({} means no attributes).type(name, bases, attrs) creates a new class dynamically. name: The class name ('Animal'). bases: Parent classes (empty () means no inheritance). attrs: Attributes and methods ({} means no attributes).
Enter fullscreen mode Exit fullscreen mode
Adding attributes and methods dynamically
<span>Animal</span> <span>=</span> <span>type</span><span>(</span><span>'</span><span>Animal</span><span>'</span><span>,</span> <span>(),</span> <span>{</span><span>'</span><span>species</span><span>'</span><span>:</span> <span>'</span><span>Mammal</span><span>'</span><span>,</span> <span>'</span><span>speak</span><span>'</span><span>:</span> <span>lambda</span> <span>self</span><span>:</span> <span>"</span><span>Roar!</span><span>"</span><span>})</span><span>a</span> <span>=</span> <span>Animal</span><span>()</span><span>print</span><span>(</span><span>a</span><span>.</span><span>species</span><span>)</span> <span># Mammal </span><span>print</span><span>(</span><span>a</span><span>.</span><span>speak</span><span>())</span> <span># Roar! </span><span>Animal</span> <span>=</span> <span>type</span><span>(</span><span>'</span><span>Animal</span><span>'</span><span>,</span> <span>(),</span> <span>{</span><span>'</span><span>species</span><span>'</span><span>:</span> <span>'</span><span>Mammal</span><span>'</span><span>,</span> <span>'</span><span>speak</span><span>'</span><span>:</span> <span>lambda</span> <span>self</span><span>:</span> <span>"</span><span>Roar!</span><span>"</span><span>})</span> <span>a</span> <span>=</span> <span>Animal</span><span>()</span> <span>print</span><span>(</span><span>a</span><span>.</span><span>species</span><span>)</span> <span># Mammal </span><span>print</span><span>(</span><span>a</span><span>.</span><span>speak</span><span>())</span> <span># Roar! </span>Animal = type('Animal', (), {'species': 'Mammal', 'speak': lambda self: "Roar!"}) a = Animal() print(a.species) # Mammal print(a.speak()) # Roar!
Enter fullscreen mode Exit fullscreen mode
Why is this powerful?
We create classes on the fly, useful for dynamic applications and metaprogramming.We create classes on the fly, useful for dynamic applications and metaprogramming.We create classes on the fly, useful for dynamic applications and metaprogramming.
Enter fullscreen mode Exit fullscreen mode
3️⃣ Defining a Custom Metaclass
A metaclass is a class that controls how classes are created.
To define one, inherit from type and override new or init.
Custom Metaclass Example
<span>class</span> <span>MyMeta</span><span>(</span><span>type</span><span>):</span><span>def</span> <span>__new__</span><span>(</span><span>cls</span><span>,</span> <span>name</span><span>,</span> <span>bases</span><span>,</span> <span>dct</span><span>):</span><span>print</span><span>(</span><span>f</span><span>"</span><span>Creating class: </span><span>{</span><span>name</span><span>}</span><span>"</span><span>)</span><span>return</span> <span>super</span><span>().</span><span>__new__</span><span>(</span><span>cls</span><span>,</span> <span>name</span><span>,</span> <span>bases</span><span>,</span> <span>dct</span><span>)</span><span>class</span> <span>MyClass</span><span>(</span><span>metaclass</span><span>=</span><span>MyMeta</span><span>):</span><span>pass</span><span>class</span> <span>MyMeta</span><span>(</span><span>type</span><span>):</span> <span>def</span> <span>__new__</span><span>(</span><span>cls</span><span>,</span> <span>name</span><span>,</span> <span>bases</span><span>,</span> <span>dct</span><span>):</span> <span>print</span><span>(</span><span>f</span><span>"</span><span>Creating class: </span><span>{</span><span>name</span><span>}</span><span>"</span><span>)</span> <span>return</span> <span>super</span><span>().</span><span>__new__</span><span>(</span><span>cls</span><span>,</span> <span>name</span><span>,</span> <span>bases</span><span>,</span> <span>dct</span><span>)</span> <span>class</span> <span>MyClass</span><span>(</span><span>metaclass</span><span>=</span><span>MyMeta</span><span>):</span> <span>pass</span>class MyMeta(type): def __new__(cls, name, bases, dct): print(f"Creating class: {name}") return super().__new__(cls, name, bases, dct) class MyClass(metaclass=MyMeta): pass
Enter fullscreen mode Exit fullscreen mode
Output:
Creating class: MyClassCreating class: MyClassCreating class: MyClass
Enter fullscreen mode Exit fullscreen mode
How does it work?
MyMeta inherits from type, making it a metaclass.__new__ is executed when the class is created (before any instance exists).MyClass is an instance of MyMeta, just as objects are instances of MyClass.MyMeta inherits from type, making it a metaclass. __new__ is executed when the class is created (before any instance exists). MyClass is an instance of MyMeta, just as objects are instances of MyClass.MyMeta inherits from type, making it a metaclass. __new__ is executed when the class is created (before any instance exists). MyClass is an instance of MyMeta, just as objects are instances of MyClass.
Enter fullscreen mode Exit fullscreen mode
4️⃣ Preventing Inheritance Using Metaclasses
Metaclasses can enforce rules, such as preventing a class from being inherited.
Example: Creating a non-inheritable class
<span>class</span> <span>NoInheritanceMeta</span><span>(</span><span>type</span><span>):</span><span>def</span> <span>__new__</span><span>(</span><span>cls</span><span>,</span> <span>name</span><span>,</span> <span>bases</span><span>,</span> <span>dct</span><span>):</span><span>if</span> <span>bases</span><span>:</span> <span># Prevent any subclassing </span> <span>raise</span> <span>TypeError</span><span>(</span><span>f</span><span>"</span><span>Class </span><span>{</span><span>name</span><span>}</span><span> cannot be inherited!</span><span>"</span><span>)</span><span>return</span> <span>super</span><span>().</span><span>__new__</span><span>(</span><span>cls</span><span>,</span> <span>name</span><span>,</span> <span>bases</span><span>,</span> <span>dct</span><span>)</span><span>class</span> <span>FinalClass</span><span>(</span><span>metaclass</span><span>=</span><span>NoInheritanceMeta</span><span>):</span><span>pass</span><span># Attempting to inherit from FinalClass </span><span>class</span> <span>SubClass</span><span>(</span><span>FinalClass</span><span>):</span> <span># This will raise an error </span> <span>pass</span><span>class</span> <span>NoInheritanceMeta</span><span>(</span><span>type</span><span>):</span> <span>def</span> <span>__new__</span><span>(</span><span>cls</span><span>,</span> <span>name</span><span>,</span> <span>bases</span><span>,</span> <span>dct</span><span>):</span> <span>if</span> <span>bases</span><span>:</span> <span># Prevent any subclassing </span> <span>raise</span> <span>TypeError</span><span>(</span><span>f</span><span>"</span><span>Class </span><span>{</span><span>name</span><span>}</span><span> cannot be inherited!</span><span>"</span><span>)</span> <span>return</span> <span>super</span><span>().</span><span>__new__</span><span>(</span><span>cls</span><span>,</span> <span>name</span><span>,</span> <span>bases</span><span>,</span> <span>dct</span><span>)</span> <span>class</span> <span>FinalClass</span><span>(</span><span>metaclass</span><span>=</span><span>NoInheritanceMeta</span><span>):</span> <span>pass</span> <span># Attempting to inherit from FinalClass </span><span>class</span> <span>SubClass</span><span>(</span><span>FinalClass</span><span>):</span> <span># This will raise an error </span> <span>pass</span>class NoInheritanceMeta(type): def __new__(cls, name, bases, dct): if bases: # Prevent any subclassing raise TypeError(f"Class {name} cannot be inherited!") return super().__new__(cls, name, bases, dct) class FinalClass(metaclass=NoInheritanceMeta): pass # Attempting to inherit from FinalClass class SubClass(FinalClass): # This will raise an error pass
Enter fullscreen mode Exit fullscreen mode
Output:
TypeError: Class SubClass cannot be inherited!TypeError: Class SubClass cannot be inherited!TypeError: Class SubClass cannot be inherited!
Enter fullscreen mode Exit fullscreen mode
Why is this useful?
It prevents unintended subclassing of critical base classes.Used in frameworks and libraries where some classes should not be extended.It prevents unintended subclassing of critical base classes. Used in frameworks and libraries where some classes should not be extended.It prevents unintended subclassing of critical base classes. Used in frameworks and libraries where some classes should not be extended.
Enter fullscreen mode Exit fullscreen mode
5️⃣ Real-World Use Cases of Metaclasses
Metaclasses are useful for:
️
Validating class attributes before the class is created.
️
Enforcing coding standards (e.g., attribute naming conventions).
️
Automatic method injection (adding methods dynamically).
️
Registering classes dynamically (for plugins, frameworks, etc.).
️
Preventing inheritance for security or design reasons.
Use Case: Auto-Registering Classes
<span>registry</span> <span>=</span> <span>{}</span><span>class</span> <span>AutoRegisterMeta</span><span>(</span><span>type</span><span>):</span><span>def</span> <span>__new__</span><span>(</span><span>cls</span><span>,</span> <span>name</span><span>,</span> <span>bases</span><span>,</span> <span>dct</span><span>):</span><span>new_class</span> <span>=</span> <span>super</span><span>().</span><span>__new__</span><span>(</span><span>cls</span><span>,</span> <span>name</span><span>,</span> <span>bases</span><span>,</span> <span>dct</span><span>)</span><span>registry</span><span>[</span><span>name</span><span>]</span> <span>=</span> <span>new_class</span> <span># Auto-register class </span> <span>return</span> <span>new_class</span><span>class</span> <span>Base</span><span>(</span><span>metaclass</span><span>=</span><span>AutoRegisterMeta</span><span>):</span><span>pass</span><span>class</span> <span>A</span><span>(</span><span>Base</span><span>):</span> <span>pass</span><span>class</span> <span>B</span><span>(</span><span>Base</span><span>):</span> <span>pass</span><span>print</span><span>(</span><span>registry</span><span>)</span><span>registry</span> <span>=</span> <span>{}</span> <span>class</span> <span>AutoRegisterMeta</span><span>(</span><span>type</span><span>):</span> <span>def</span> <span>__new__</span><span>(</span><span>cls</span><span>,</span> <span>name</span><span>,</span> <span>bases</span><span>,</span> <span>dct</span><span>):</span> <span>new_class</span> <span>=</span> <span>super</span><span>().</span><span>__new__</span><span>(</span><span>cls</span><span>,</span> <span>name</span><span>,</span> <span>bases</span><span>,</span> <span>dct</span><span>)</span> <span>registry</span><span>[</span><span>name</span><span>]</span> <span>=</span> <span>new_class</span> <span># Auto-register class </span> <span>return</span> <span>new_class</span> <span>class</span> <span>Base</span><span>(</span><span>metaclass</span><span>=</span><span>AutoRegisterMeta</span><span>):</span> <span>pass</span> <span>class</span> <span>A</span><span>(</span><span>Base</span><span>):</span> <span>pass</span> <span>class</span> <span>B</span><span>(</span><span>Base</span><span>):</span> <span>pass</span> <span>print</span><span>(</span><span>registry</span><span>)</span>registry = {} class AutoRegisterMeta(type): def __new__(cls, name, bases, dct): new_class = super().__new__(cls, name, bases, dct) registry[name] = new_class # Auto-register class return new_class class Base(metaclass=AutoRegisterMeta): pass class A(Base): pass class B(Base): pass print(registry)
Enter fullscreen mode Exit fullscreen mode
Output:
<span>{</span><span>'Base'</span>: <class <span>'__main__.Base'</span><span>></span>, <span>'A'</span>: <class <span>'__main__.A'</span><span>></span>, <span>'B'</span>: <class <span>'__main__.B'</span><span>>}</span><span>{</span><span>'Base'</span>: <class <span>'__main__.Base'</span><span>></span>, <span>'A'</span>: <class <span>'__main__.A'</span><span>></span>, <span>'B'</span>: <class <span>'__main__.B'</span><span>>}</span>{'Base': <class '__main__.Base'>, 'A': <class '__main__.A'>, 'B': <class '__main__.B'>}
Enter fullscreen mode Exit fullscreen mode
How is this useful?
It’s commonly used in plugin systems, where classes register themselves dynamically.<br>It’s commonly used in plugin systems, where classes register themselves dynamically.<br>It’s commonly used in plugin systems, where classes register themselves dynamically.
Enter fullscreen mode Exit fullscreen mode
6️⃣ When Should You Use Metaclasses?
Use metaclasses when you need to modify class behavior dynamically.
They are useful for frameworks, libraries, and plugin-based systems.
Use them to enforce constraints, like preventing inheritance.
Avoid metaclasses unless necessary—they make code more complex.
7️⃣ Summary
Metaclasses control class creation in Python.
All classes are instances of type (which is itself a metaclass).
We can create classes dynamically using type(name, bases, attrs).
Custom metaclasses can modify attributes, enforce rules, and auto-register classes.
Metaclasses can prevent inheritance when needed.
Metaclasses are powerful but should be used wisely!
What’s Next?
Now that we’ve explored metaclasses, in the next post, we’ll dive into Python’s multiple inheritance and method resolution order (MRO)! Stay tuned.
Got questions? Drop them in the comments!
原文链接:Metaclasses in Python – The Power Behind Class Creation
暂无评论内容