Python7 min read

Python Descriptors

Control attribute access with descriptors.

David Miller
December 18, 2025
0.0k0

Low-level attribute control.

Basic Descriptor

```python class Positive: def __init__(self, name): self.name = name def __get__(self, obj, objtype=None): return obj.__dict__.get(self.name, 0) def __set__(self, obj, value): if value < 0: raise ValueError("Must be positive") obj.__dict__[self.name] = value

class Account: balance = Positive("balance") def __init__(self, balance): self.balance = balance

account = Account(1000) print(account.balance) # 1000 # account.balance = -500 # Error! ```

Type Validation

```python class TypedProperty: def __init__(self, name, expected_type): self.name = name self.expected_type = expected_type def __get__(self, obj, objtype=None): if obj is None: return self return obj.__dict__.get(self.name) def __set__(self, obj, value): if not isinstance(value, self.expected_type): raise TypeError(f"Expected {self.expected_type}") obj.__dict__[self.name] = value

class Person: name = TypedProperty("name", str) age = TypedProperty("age", int) def __init__(self, name, age): self.name = name self.age = age

person = Person("Tom", 25) # person.age = "25" # Error! Must be int ```

Cached Property

```python class CachedProperty: def __init__(self, func): self.func = func def __get__(self, obj, objtype=None): if obj is None: return self # Cache result value = self.func(obj) obj.__dict__[self.func.__name__] = value return value

class DataProcessor: @CachedProperty def expensive_calculation(self): print("Computing...") return sum(range(1000000))

processor = DataProcessor() print(processor.expensive_calculation) # Computing... 499999500000 print(processor.expensive_calculation) # 499999500000 (cached) ```

Remember

- Descriptors control attribute access - Properties are built on descriptors - Very powerful but complex

#Python#Advanced#OOP