What are Python descriptors and how do they underpin properties and ORM fields?
py-sen-003
Your answer
Answer as you would in a real interview — explain your thinking, not just the conclusion.
Model answer
A descriptor is an object that implements __get__, __set__, or __delete__ and is assigned to a class attribute. Python's attribute lookup protocol checks the class's MRO for descriptors before returning instance __dict__ entries. Data descriptors (define both __get__ and __set__) take priority over instance variables. Non-data descriptors (only __get__) have lower priority. property is a built-in data descriptor. Django's ORM fields (CharField, IntegerField) are descriptors — this is how model.title = 'foo' can trigger validation or deferred loading without the caller knowing. staticmethod and classmethod are non-data descriptors. Understanding descriptors is essential for writing frameworks, ORMs, and advanced metaclass-based systems.
Code example
class Validated:
"""Data descriptor: validates that an attribute is a non-empty string."""
def __set_name__(self, owner, name):
# Called when assigned to a class; stores the attribute name
self._name = name
self._private = f"_{name}"
def __get__(self, obj, objtype=None):
if obj is None:
return self # accessed on class, not instance
return getattr(obj, self._private, None)
def __set__(self, obj, value: str):
if not isinstance(value, str) or not value.strip():
raise ValueError(f"{self._name} must be a non-empty string")
setattr(obj, self._private, value.strip())
def __delete__(self, obj):
delattr(obj, self._private)
class User:
name = Validated()
email = Validated()
u = User()
u.name = "Alice" # triggers __set__
print(u.name) # triggers __get__ -> "Alice"
try:
u.email = "" # raises ValueError
except ValueError as e:
print(e) # email must be a non-empty string
Follow-up
What is the difference between __set_name__ and __init__ in a descriptor, and when was __set_name__ added?