Python Descriptors Properties Slots & Data Binding

Zaheer Ahmad 5 min read min read
Python
Python Descriptors Properties Slots & Data Binding

Introduction

Python is one of the most popular programming languages in Pakistan, widely used in web development, AI, data science, and automation. One of the most powerful but often misunderstood advanced features in Python is Python descriptors: properties, slots & data binding.

A descriptor is a special Python object that allows you to control how attributes are accessed, set, or deleted in a class. This is the foundation behind features like @property, method binding, and even frameworks like Django ORM.

For Pakistani students learning advanced Python, mastering descriptors is extremely valuable because:

  • It improves understanding of how Python internally manages objects
  • It helps you write cleaner and more secure code
  • It is heavily used in real-world frameworks (Django, Flask extensions, data validation libraries)

In short, if you want to become an advanced Python developer in Lahore, Karachi, or Islamabad job markets, descriptors are a must-know concept.

Prerequisites

Before learning Python descriptors, you should already understand:

  • Basic Python classes and objects
  • Object-Oriented Programming (OOP) concepts
  • Functions and decorators
  • Basic understanding of __init__ method
  • Python dictionaries and attribute access

If you are not comfortable with these topics, first review:

  • Python OOP fundamentals on theiqra.edu.pk
  • Python functions and decorators tutorial

Core Concepts & Explanation

Descriptor Protocol (get, set, delete)

A descriptor is any object that implements at least one of these methods:

  • __get__(self, instance, owner)
  • __set__(self, instance, value)
  • __delete__(self, instance)

These methods control how attribute access works.

How it works:

When you access an attribute like:

obj.name

Python internally checks:

  1. Is name a descriptor?
  2. Does it have __get__?
  3. If yes, call __get__ instead of returning raw value

Simple Descriptor Example:

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

    def __set__(self, instance, value):
        instance._name = value.upper()

Line-by-line explanation:

  • class UpperCaseString: → Defines a descriptor class
  • __get__ → Runs when we access the attribute
  • instance._name → Stores actual data inside object
  • __set__ → Converts input to uppercase before saving
  • value.upper() → Ensures data consistency

Python property as Descriptor Shortcut

In Python, the @property decorator is just a built-in descriptor implementation.

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

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = value.title()

Explanation:

  • @property → Acts like __get__
  • @name.setter → Acts like __set__
  • _name → private storage variable

This is why python property descriptor is considered a simplified version of full descriptors.


Practical Code Examples

Example 1: Data Validation Descriptor

Let’s create a descriptor that ensures age is always between 0 and 120.

class AgeValidator:
    def __set_name__(self, owner, name):
        self.name = name

    def __get__(self, instance, owner):
        return instance.__dict__.get(self.name)

    def __set__(self, instance, value):
        if not (0 <= value <= 120):
            raise ValueError("Age must be between 0 and 120")
        instance.__dict__[self.name] = value


class Person:
    age = AgeValidator()

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

Line-by-line explanation:

  • class AgeValidator: → Descriptor for validation
  • __set_name__ → Automatically gets attribute name (age)
  • instance.__dict__ → Stores actual values safely
  • if not (0 <= value <= 120) → Validates age range
  • raise ValueError → Stops invalid data
  • Person.age = AgeValidator() → Attaches descriptor

Now usage:

p = Person("Ahmad", 25)
print(p.age)

If invalid:

p.age = 200  # Error

Example 2: Real-World Application (Bank Account System)

Imagine a banking system in Pakistan where users from Karachi, Lahore, and Islamabad manage accounts.

class BalanceDescriptor:
    def __init__(self):
        self.balance = {}

    def __get__(self, instance, owner):
        return self.balance.get(instance, 0)

    def __set__(self, instance, value):
        if value < 0:
            raise ValueError("Balance cannot be negative")
        self.balance[instance] = value


class BankAccount:
    balance = BalanceDescriptor()

    def __init__(self, owner, balance):
        self.owner = owner
        self.balance = balance

Explanation:

  • self.balance = {} → Stores balances per object
  • instance → Each account object (Ahmad, Fatima, Ali)
  • Prevents negative balances
  • Central control of attribute logic

Example usage:

acc1 = BankAccount("Ahmad", 5000)
acc2 = BankAccount("Fatima", 10000)

print(acc1.balance)

Common Mistakes & How to Avoid Them

Mistake 1: Overwriting Descriptor in Instance Dictionary

Problem:

class Demo:
    value = AgeValidator()

obj = Demo()
obj.value = 50  # overwrites descriptor

Why it happens:

Instance dictionary takes priority over descriptor.

Fix:

Always store values inside instance.__dict__ or descriptor-managed storage.


Mistake 2: Confusing slots with Descriptors

Many students think __slots__ is a descriptor — it is not.

class Student:
    __slots__ = ['name', 'age']

Explanation:

  • __slots__ reduces memory usage
  • It does NOT manage attribute logic
  • Descriptors control behavior, slots control storage

Practice Exercises

Exercise 1: Create a Marks Validator

Task:
Create a descriptor that only allows marks between 0 and 100.

Solution:

class Marks:
    def __set_name__(self, owner, name):
        self.name = name

    def __get__(self, instance, owner):
        return instance.__dict__.get(self.name)

    def __set__(self, instance, value):
        if not (0 <= value <= 100):
            raise ValueError("Invalid marks")
        instance.__dict__[self.name] = value


class Student:
    marks = Marks()

Exercise 2: Temperature Converter Descriptor

Task:
Store temperature in Celsius but allow input in Fahrenheit.

Solution:

class Temperature:
    def __get__(self, instance, owner):
        return instance._celsius

    def __set__(self, instance, value):
        instance._celsius = (value - 32) * 5/9


class Weather:
    temp = Temperature()

Frequently Asked Questions

What is a Python descriptor?

A descriptor is an object that defines how attribute access works using __get__, __set__, or __delete__. It allows controlled access to class attributes.


What is a python property descriptor?

A property descriptor is a simplified descriptor created using @property, which internally uses __get__ and __set__.


What is the use of get set in Python?

They are methods that control attribute reading and writing. __get__ is called when accessing an attribute, and __set__ is called when assigning a value.


How do descriptors relate to data binding?

Descriptors enable data binding by automatically updating or validating values whenever attributes change in an object.


Why are descriptors important in Python?

They are used in frameworks like Django, ORM systems, and validation libraries. They help build reusable and controlled attribute behavior.


Summary & Key Takeaways

  • Descriptors control attribute access in Python using __get__, __set__, and __delete__
  • @property is a built-in descriptor shortcut
  • They are widely used in real-world frameworks
  • __slots__ reduces memory usage but is not a descriptor
  • Descriptors help implement validation, data binding, and computed attributes
  • They improve code reusability and maintainability

To strengthen your Python skills further, explore:

  • Learn more about Python OOP concepts on theiqra.edu.pk
  • Master advanced Python with Python Metaclasses tutorial
  • Understand decorators in depth with Python Decorators Guide
  • Explore Django model fields and validation internals

Mastering descriptors will put you on a professional Python developer path, especially for backend and AI development roles in Pakistan’s growing tech industry.

Practice the code examples from this tutorial
Open Compiler
Share this tutorial:

Test Your Python Knowledge!

Finished reading? Take a quick quiz to see how much you've learned from this tutorial.

Start Python Quiz

About Zaheer Ahmad