Advanced Python Metaclass and Descriptor Techniques

Dynamic Properties with @property

The @property decorator alllows methods to behave like attributes. A getter retrieves a computed value, while a setter enables cnotrolled assignment.

from datetime import datetime, date

class Person:
    def __init__(self, full_name, birth_date):
        self.full_name = full_name
        self.birth_date = birth_date
        self._cached_age = None

    @property
    def age(self):
        return datetime.now().year - self.birth_date.year

    @age.setter
    def age(self, years):
        self._cached_age = years

if __name__ == "__main__":
    individual = Person("Alice", date(1990, 5, 15))
    individual.age = 33
    print(individual._cached_age)  # 33
    print(individual.age)          # Current computed age

Attribute Access with __getattr__ and __getattribute__

__getattr__ is invoked only when an attribute is not found via normal lookup. In contrast, __getattribute__ intercepts all attribute access and has higher precedence.

class Profile:
    def __init__(self, metadata):
        self.metadata = metadata

    def __getattr__(self, key):
        return self.metadata.get(key)

# Example usage
person = Profile({"job": "Engineer", "city": "Berlin"})
print(person.job)  # 'Engineer'

class LockedProfile:
    def __getattribute__(self, name):
        return "restricted"

obj = LockedProfile()
print(obj.any_attr)  # 'restricted'

Descriptors and Attribute Lookup Order

A descriptor is any object defining __get__, __set__, or __delete__. Data descriptors (with __set__) take precedence over instance dictionaries.

import numbers

class PositiveInteger:
    def __set_name__(self, owner, name):
        self.private_name = f'_{name}'

    def __get__(self, instance, owner):
        if instance is None:
            return self
        return getattr(instance, self.private_name)

    def __set__(self, instance, value):
        if not isinstance(value, numbers.Integral):
            raise TypeError("Must be an integer")
        if value <= 0:
            raise ValueError("Must be positive")
        setattr(instance, self.private_name, value)

class Student:
    score = PositiveInteger()

s = Student()
s.score = 95
print(s.score)  # 95

Attribute resolution follows this order:

  1. Data descriptor in class or base class → call __get__
  2. Instance dictionary (obj.__dict__)
  3. Non-data descriptor or regular class attribute
  4. __getattr__ if defined
  5. Raise AttributeError

Difference Between __new__ and __init__

__new__ controls object creation and must return an instance. __init__ initializes the returned isntance. __init__ is only called if __new__ returns an instance of the class.

class Singleton:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self, identifier):
        self.identifier = identifier

a = Singleton("first")
b = Singleton("second")
print(a is b)  # True

Dynamic Class Creation with type

The built-in type can create classes dynamically at runtime.

def greet(self):
    return f"Hello, I'm {self.name}"

Base = type('Base', (), {'respond': lambda self: "Base response"})
DynamicUser = type('DynamicUser', (Base,), {
    'name': 'Dyna',
    'greet': greet
})

user = DynamicUser()
print(user.greet())      # Hello, I'm Dyna
print(user.respond())    # Base response

Custom Metaclasses

A metaclass defines how a class is created. By inheriting from type, you can customize class construction.

class VerboseMeta(type):
    def __new__(mcs, name, bases, namespace, **kwds):
        print(f"Creating class {name}")
        return super().__new__(mcs, name, bases, namespace)

class Widget(metaclass=VerboseMeta):
    pass
# Output: Creating class Widget

Implementing a Simple ORM Using Metaclasses

Metaclasses enable framework-level features like Django’s ORM by inspecting class definitions at creation time.

import numbers

class Field:
    pass

class IntegerField(Field):
    def __init__(self, column=None, min_val=0, max_val=2**31-1):
        self.column = column
        self.min_val = min_val
        self.max_val = max_val

    def __set_name__(self, owner, name):
        self.attr_name = name
        if self.column is None:
            self.column = name

    def __get__(self, instance, owner):
        if instance is None:
            return self
        return instance.__dict__.get(self.attr_name)

    def __set__(self, instance, value):
        if not isinstance(value, numbers.Integral):
            raise TypeError("Integer required")
        if not (self.min_val <= value <= self.max_val):
            raise ValueError(f"Value must be between {self.min_val} and {self.max_val}")
        instance.__dict__[self.attr_name] = value

class StringField(Field):
    def __init__(self, column=None, max_len=255):
        if max_len <= 0:
            raise ValueError("max_len must be positive")
        self.column = column
        self.max_len = max_len

    def __set_name__(self, owner, name):
        self.attr_name = name
        if self.column is None:
            self.column = name

    def __get__(self, instance, owner):
        if instance is None:
            return self
        return instance.__dict__.get(self.attr_name)

    def __set__(self, instance, value):
        if not isinstance(value, str):
            raise TypeError("String required")
        if len(value) > self.max_len:
            raise ValueError(f"Exceeds max length of {self.max_len}")
        instance.__dict__[self.attr_name] = value

class ModelMeta(type):
    def __new__(mcs, name, bases, attrs):
        if name == "Model":
            return super().__new__(mcs, name, bases, attrs)
        
        fields = {}
        for key, val in list(attrs.items()):
            if isinstance(val, Field):
                fields[key] = val
                del attrs[key]
        
        table_name = attrs.get('__tablename__', name.lower())
        attrs['_fields'] = fields
        attrs['_table'] = table_name
        return super().__new__(mcs, name, bases, attrs)

class Model(metaclass=ModelMeta):
    def __init__(self, **kwargs):
        for field_name in self._fields:
            if field_name in kwargs:
                setattr(self, field_name, kwargs[field_name])

    def save(self):
        cols = []
        vals = []
        for name, field in self._fields.items():
            cols.append(field.column)
            val = getattr(self, name, None)
            vals.append(repr(val) if isinstance(val, str) else str(val))
        sql = f"INSERT INTO {self._table} ({', '.join(cols)}) VALUES ({', '.join(vals)})"
        print(sql)

class Employee(Model):
    __tablename__ = "employees"
    name = StringField(max_len=50)
    salary = IntegerField(min_val=0, max_val=1000000)

emp = Employee(name="John Doe", salary=75000)
emp.save()
# Output: INSERT INTO employees (name, salary) VALUES ('John Doe', 75000)

Tags: python metaclass descriptor ORM property

Posted on Thu, 22 Oct 2026 16:08:40 +0000 by alanho