💡 Advanced Python Concepts
Estimated reading: 4 minutes 44 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 :

Leave a Reply

Your email address will not be published. Required fields are marked *

Share

Python Descriptors

Or Copy Link

CONTENTS
Scroll to Top