pythonadvanced

Descriptors and the Property Pattern

Use Python descriptors and @property for controlled attribute access.

python
# Simple @property
class Circle:
    def __init__(self, radius: float):
        self.radius = radius  # triggers setter

    @property
    def radius(self) -> float:
        return self._radius

    @radius.setter
    def radius(self, value: float):
        if value < 0:
            raise ValueError("Radius must be non-negative")
        self._radius = float(value)

    @property
    def area(self) -> float:
        import math
        return math.pi * self._radius ** 2

# Reusable descriptor
class Validated:
    def __init__(self, min_val=None, max_val=None):
        self.min_val = min_val
        self.max_val = max_val

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

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

    def __set__(self, obj, value):
        if self.min_val is not None and value < self.min_val:
            raise ValueError(f"Must be >= {self.min_val}")
        if self.max_val is not None and value > self.max_val:
            raise ValueError(f"Must be <= {self.max_val}")
        setattr(obj, self.name, value)

class Product:
    price = Validated(min_val=0)
    quantity = Validated(min_val=0, max_val=10000)

p = Product()
p.price = 29.99
p.quantity = 100

Use Cases

  • Controlled attribute access
  • Validation patterns
  • Computed properties

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.