💡 Advanced Python Concepts
Estimated reading: 4 minutes 277 views

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:

  • @property decorators
  • 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

MethodTriggered By
__get__()Attribute access (obj.x)
__set__()Attribute assignment (obj.x = val)
__delete__()del obj.x

Data vs Non-Data Descriptors

TypeImplementsOverrides Instance Dictionary?
Data Descriptor__get__ and __set__ Yes
Non-Data DescriptorOnly __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@propertyDescriptor
SyntaxMethod decoratorSeparate class
ReusableNo (per-property definition)Yes (general-purpose field classes)
FlexibilityBasic getters/settersFull 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 logicUsing property when multiple reuse needed
Prefer __dict__ for storageCreating recursive calls in __get__
Implement __set_name__() for cleaner setupHardcoding attribute names
Combine with metaclasses or decoratorsNesting 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 :
Share

Python Descriptors

Or Copy Link

CONTENTS
Scroll to Top