Menu
Courses / python advanced / Metaclasses & Descriptors

Metaclasses & Descriptors

05 / 10 Part of python advanced




 

    🎓 Advanced · Topic 05
 

 


    Metaclasses & Descriptors
 


 


    Go behind the scenes of Python's object model. Control how classes are created with metaclasses and how attribute access works with descriptors.
 


 

    ⏱ ~60 min
    🔴 Advanced
    🔬 Object model
 


 
 

   

     
     

type — The Metaclass of All Classes


   

   

In Python, classes are objects too. type is the class that creates classes. You can use it dynamically to build classes at runtime.


   

     

        type_basics.py
     

     
print(type(42))        # <class 'int'>
print(type(int))       # <class 'type'>
print(type(type))      # <class 'type'>

# Dynamically create a class with type(name, bases, dict)
Dog = type("Dog", (), {
    "sound": "Woof",
    "bark": lambda self: print(self.sound),
})

rex = Dog()
rex.bark()   # Woof

   

 


 
 

   

     
     

Custom Metaclasses


   

   

Override __new__ or __init__ on a metaclass to intercept and modify class creation — perfect for ORMs, API frameworks, and plugin systems.


   

     

        metaclass_example.py
     

     
class Singleton(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 Config(metaclass=Singleton):
    def __init__(self):
        self.debug = False

a = Config()
b = Config()
print(a is b)   # True — same instance

   

   

      ⚠️ Use metaclasses sparingly
     

Tim Peters: "If you wonder whether you need a metaclass, you don't." Prefer class decorators or __init_subclass__ for most use cases.


   

 


 
 

   

     
     

Descriptors


   

   

A descriptor is an object that defines __get__, __set__, or __delete__. This is how @property, classmethod, and Django/SQLAlchemy fields work under the hood.


   

     

        descriptor.py
     

     
class Validated:
    """Descriptor that enforces a minimum value."""
    def __init__(self, minimum):
        self.minimum = minimum
        self.attr    = None

    def __set_name__(self, owner, name):
        self.attr = f"_{name}"

    def __get__(self, obj, objtype=None):
        return getattr(obj, self.attr, None)

    def __set__(self, obj, value):
        if value < self.minimum:
            raise ValueError(f"Must be >= {self.minimum}")
        setattr(obj, self.attr, value)

class Product:
    price = Validated(minimum=0)
    stock = Validated(minimum=0)

p = Product()
p.price = 9.99   # ok
p.price = -1    # ValueError: Must be >= 0

   

 


 
 

   

     
     

__init_subclass__ — Lightweight Hook


   

   

A simpler alternative to metaclasses for hooking into subclass creation.


   

     

        init_subclass.py
     

     
class Plugin:
    _registry = {}

    def __init_subclass__(cls, name, **kwargs):
        super().__init_subclass__(**kwargs)
        cls._registry[name] = cls

class JSONPlugin(Plugin, name="json"): pass
class CSVPlugin(Plugin,  name="csv"):  pass

print(Plugin._registry)
# {'json': JSONPlugin, 'csv': CSVPlugin}