python 37 lines · 8 steps

Building a typed descriptor in Python

A data descriptor that enforces types and runs custom validators on every attribute assignment.

Explained by highlit
1class Typed:
2 """A data descriptor that enforces a type and optional validation."""
3 
4 def __init__(self, expected_type, validator=None):
5 self.expected_type = expected_type
6 self.validator = validator
7 
8 def __set_name__(self, owner, name):
9 self.name = name
10 self.private_name = f"_{name}"
11 
12 def __get__(self, instance, owner=None):
13 if instance is None:
14 return self
15 return getattr(instance, self.private_name)
16 
17 def __set__(self, instance, value):
18 if not isinstance(value, self.expected_type):
19 raise TypeError(
20 f"{self.name!r} must be {self.expected_type.__name__}, "
21 f"got {type(value).__name__}"
22 )
23 if self.validator is not None and not self.validator(value):
24 raise ValueError(f"{value!r} failed validation for {self.name!r}")
25 setattr(instance, self.private_name, value)
26 
27 def __delete__(self, instance):
28 raise AttributeError(f"{self.name!r} cannot be deleted")
29 
30 
31class Account:
32 owner = Typed(str, validator=lambda s: len(s) > 0)
33 balance = Typed((int, float), validator=lambda n: n >= 0)
34 
35 def __init__(self, owner, balance):
36 self.owner = owner
37 self.balance = balance
01 / 01
STEP 01

Walkthrough

Space play ←→ step click any line
Three takeaways
  1. 1Data descriptors intercept attribute access at the class level, letting you centralize validation logic instead of scattering checks across setters.
  2. 2__set_name__ hands each descriptor its own attribute name automatically, so one descriptor class can be reused for many fields.
  3. 3Storing values under a per-name private attribute keeps the descriptor's bookkeeping separate from the public attribute it guards.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Building a typed descriptor in Python — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code