Python Descriptors – Master Attribute Access and Control
Introduction – Why Use Descriptors?
Python classes offer flexible ways to control how attributes are accessed, modified, and deleted. If you’ve ever used:
@propertydecorators- ORMs like SQLAlchemy
- Data validation patterns
…you’ve already used descriptors, possibly without realizing it.
Descriptors give you low-level control over attribute behavior and are the building blocks for Python’s property system, class decorators, and data management frameworks.
In this guide, you’ll learn:
- What descriptors are and how they work
- How to implement
__get__,__set__, and__delete__ - The difference between data and non-data descriptors
- Real-world use cases and best practices
What Is a Descriptor?
A descriptor is a class that implements any of the descriptor methods:
__get__(self, instance, owner)__set__(self, instance, value)__delete__(self, instance)
Descriptors are defined at the class level and control attribute access at the instance level.
Creating a Simple Descriptor
class Descriptor:
def __get__(self, instance, owner):
return "Value from descriptor"
class MyClass:
attr = Descriptor()
obj = MyClass()
print(obj.attr) # Value from descriptor
The descriptor’s __get__() is triggered when accessing obj.attr.
Full Descriptor with __get__ and __set__
class Name:
def __init__(self):
self._value = ""
def __get__(self, instance, owner):
return self._value
def __set__(self, instance, value):
if not isinstance(value, str):
raise ValueError("Name must be a string")
self._value = value
class Person:
name = Name()
p = Person()
p.name = "Alice"
print(p.name) # Alice
You can enforce data validation and custom logic during attribute assignment.
Descriptor Methods Overview
| Method | Triggered By |
|---|---|
__get__() | Attribute access (obj.x) |
__set__() | Attribute assignment (obj.x = val) |
__delete__() | del obj.x |
Data vs Non-Data Descriptors
| Type | Implements | Overrides Instance Dictionary? |
|---|---|---|
| Data Descriptor | __get__ and __set__ | Yes |
| Non-Data Descriptor | Only __get__ | No – instance dict wins |
Example:
class NonData:
def __get__(self, instance, owner):
return "non-data"
class MyClass:
attr = NonData()
obj = MyClass()
obj.__dict__['attr'] = "instance attr"
print(obj.attr) # instance attr (wins over non-data descriptor)
Non-data descriptors are overridden by instance attributes.
Real-World Use Case – Typed Fields
class Typed:
def __init__(self, name, expected_type):
self.name = name
self.expected_type = expected_type
def __get__(self, instance, owner):
return instance.__dict__[self.name]
def __set__(self, instance, value):
if not isinstance(value, self.expected_type):
raise TypeError(f"Expected {self.expected_type}")
instance.__dict__[self.name] = value
class Product:
name = Typed("name", str)
price = Typed("price", float)
p = Product()
p.name = "Laptop"
p.price = 999.99
Descriptors power custom validation logic at the class level—perfect for ORM fields, data models, and configuration systems.
Descriptors vs @property
| Feature | @property | Descriptor |
|---|---|---|
| Syntax | Method decorator | Separate class |
| Reusable | No (per-property definition) | Yes (general-purpose field classes) |
| Flexibility | Basic getters/setters | Full control + reuse |
Use descriptors when you need reuse, validation, or complex behavior across multiple attributes.
Best Practices
| Do This | Avoid This |
|---|---|
| Use descriptors for reusable field logic | Using property when multiple reuse needed |
Prefer __dict__ for storage | Creating recursive calls in __get__ |
Implement __set_name__() for cleaner setup | Hardcoding attribute names |
| Combine with metaclasses or decorators | Nesting descriptors without documentation |
Summary – Recap & Next Steps
Descriptors allow you to build powerful and reusable attribute management logic. They’re used in the core of Python’s own language features and widely adopted in frameworks and libraries.
Key Takeaways:
- Descriptors are classes that define
__get__,__set__, or__delete__ - Use for attribute control, validation, caching, or logging
- Data descriptors override instance attributes; non-data do not
- Ideal for frameworks, ORMs, and custom data models
Real-World Relevance:
Used in Django ORM fields, SQLAlchemy columns, Pydantic models, and property-like patterns.
FAQ – Python Descriptors
What is a Python descriptor?
A class that defines any of __get__, __set__, or __delete__ methods, used to manage attribute access.
When should I use a descriptor?
When you need reusable logic for attribute access/validation across many classes.
What’s the difference between a data and non-data descriptor?
- Data descriptors implement
__set__()(and override instance vars) - Non-data descriptors only implement
__get__()(and are overridden by instance vars)
How are descriptors used in frameworks?
Descriptors power field definitions, validation layers, and configuration systems.
What is __set_name__?
A special method added in Python 3.6+ that informs the descriptor of its attribute name:
def __set_name__(self, owner, name):
self.name = name
Share Now :
