Descriptors in Python (Magic Methods Behind the Scenes)

@amitmund June 17, 2026

Descriptors in Python (__get__, __set__, __delete__)

Descriptors are advanced Python objects that control attribute access in Python.

They are widely used in: - Django ORM fields - Pydantic validation - Flask internals - @property system


What is a Descriptor?

A descriptor is any object that defines at least one of:

  • __get__
  • __set__
  • __delete__

1. __get__ (Attribute Access)

Used when you access:

obj.value

Example:

class Descriptor:
    def __get__(self, instance, owner):
        return "Hello from descriptor"

class MyClass:
    value = Descriptor()

obj = MyClass()
print(obj.value)

Output:

Hello from descriptor

Graph

obj.value
   ↓
__get__ is called
   ↓
value returned

Real World Example (Logging Access)

class LogAccess:
    def __get__(self, instance, owner):
        print("Accessing attribute")
        return instance._name

class User:
    name = LogAccess()

    def __init__(self, name):
        self._name = name

u = User("Aman")
print(u.name)

2. __set__ (Attribute Assignment)

Used when:

obj.value = something

Example:

class Validator:
    def __set__(self, instance, value):
        if value < 18:
            raise ValueError("Age must be >= 18")
        instance._age = value

class Person:
    age = Validator()

p = Person()
p.age = 20
print(p._age)

Graph

p.age = 20
   ↓
__set__ triggered
   ↓
validation runs
   ↓
value stored

Real World Example

Used in: - Django model fields - API validation - Forms


3. __delete__ (Attribute Deletion)

Used when:

del obj.value

Example:

class SafeDelete:
    def __delete__(self, instance):
        print("Deleting value")
        del instance._name

class User:
    name = SafeDelete()

    def __init__(self, name):
        self._name = name

u = User("Aman")
del u.name

Graph

del obj.name
     ↓
__delete__ runs
     ↓
cleanup executed

4. Full Descriptor (All Methods)

class NameDescriptor:
    def __get__(self, instance, owner):
        return instance._name

    def __set__(self, instance, value):
        if not value:
            raise ValueError("Name cannot be empty")
        instance._name = value

    def __delete__(self, instance):
        print("Name deleted")
        del instance._name

class User:
    name = NameDescriptor()

    def __init__(self, name):
        self.name = name

u = User("Aman")
print(u.name)
u.name = "Ravi"
del u.name

Descriptor Flow

Attribute Access
      ↓
Python checks descriptor
      ↓
__get__ / __set__ / __delete__
      ↓
Custom logic executes

5. Real World Mapping

Action Meaning
__get__ read DB field
__set__ validate input
__delete__ cleanup/security

6. Descriptor vs @property

Feature Descriptor @property
Reusability High Low
Power Very High Medium
Framework usage Heavy Light

7. How @property works internally

@property is actually a descriptor.

Flow: @property → __get__ internally → controlled attribute access


8. Common Mistakes

Mistake 1: Missing parameters

def __get__(self):

Correct:

def __get__(self, instance, owner):

Mistake 2: Wrong storage

self.age = value   ❌

Correct:

self._age = value   ✔

Mistake 3: Using descriptor on instance

obj = Descriptor()

Correct:

class A:
    value = Descriptor()

Homework 1

Create Age Descriptor: - age must be >= 18

class Age:
    def __get__(self, instance, owner):
        return instance._age

    def __set__(self, instance, value):
        if value < 18:
            raise ValueError("Too young")
        instance._age = value

class Person:
    age = Age()

    def __init__(self, age):
        self.age = age

p = Person(20)
print(p.age)

Homework 2

Logging descriptor:

class Logger:
    def __get__(self, instance, owner):
        print("Accessed")
        return instance._value

    def __set__(self, instance, value):
        print("Updated")
        instance._value = value

class Data:
    value = Logger()

d = Data()
d.value = 100
print(d.value)

9. Real World Use Cases

System Usage
Django ORM Field validation
Flask Request handling
Pydantic Data validation
Python internals property system

10. Visual Summary

Object attribute access ↓ Descriptor protocol triggered ↓ Custom logic executes ↓ Final value returned


Next Advanced Topics

  • Metaclasses (VERY important next step)
  • Django ORM internals
  • __getattribute__ deep behavior
  • Pydantic validation system
  • Property vs Descriptor advanced patterns
0 Likes
32 Views
0 Comments

Filters

No filters available for this view.

Reset All