Python descriptors are objects that implement __get__, __set__, or __delete__ to intercept attribute access. They power properties, methods, static methods, and class variables—any time you access an attribute on an instance or class, the descriptor protocol may run.
Descriptors let you customize how attributes are stored, computed, validated, or accessed without explicit getter/setter function calls.
This example implements a descriptor that validates and stores an age attribute.
# run: python3 descriptors.py # description: descriptor for attribute validation and storage class ValidatedInt: def __init__(self, name, min_val=0, max_val=100): self.name = name self.min_val = min_val self.max_val = max_val self.data = {} def __get__(self, obj, objtype=None): if obj is None: return self return self.data.get(id(obj)) def __set__(self, obj, value): if not isinstance(value, int): raise TypeError(f"{self.name} must be int") if not (self.min_val <= value <= self.max_val): raise ValueError(f"{self.name} must be {self.min_val}-{self.max_val}") self.data[id(obj)] = value def __delete__(self, obj): del self.data[id(obj)] class Person: age = ValidatedInt("age", 0, 150) def __init__(self, name, age): self.name = name self.age = age p = Person("Alice", 30) print(f"age: {p.age}") p.age = 31 print(f"age: {p.age}") try: p.age = 200 # raises ValueError except ValueError as e: print(f"error: {e}") try: p.age = "thirty" # raises TypeError except TypeError as e: print(f"error: {e}")
Data descriptors (implement __set__ or __delete__):
__dict__
Non-data descriptors (only __get__):
Property decorator (built-in descriptor):
@property creates a data descriptor for getter@attr.setter, @attr.deleter add write/delete behaviorCommon uses: