Skip to content

Python Data Model

This guide covers Python's data model, special methods, and how they enable object behavior customization.

Table of Contents

Introduction

What is the Data Model?

The Python data model defines how objects behave in response to language operations. Special methods (dunder methods) allow objects to implement and customize these behaviors.

Key Concepts

  • Special methods (dunder methods)
  • Object protocols
  • Language integration
  • Custom behavior

Special Methods

Basic Object Operations

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return f'Vector({self.x}, {self.y})'

    def __str__(self):
        return f'({self.x}, {self.y})'

    def __abs__(self):
        return (self.x ** 2 + self.y ** 2) ** 0.5

    def __bool__(self):
        return bool(abs(self))

Container Operations

class FrenchDeck:
    ranks = [str(n) for n in range(2, 11)] + list('JQKA')
    suits = 'spades diamonds clubs hearts'.split()

    def __init__(self):
        self._cards = [Card(rank, suit) 
                      for suit in self.suits 
                      for rank in self.ranks]

    def __len__(self):
        return len(self._cards)

    def __getitem__(self, position):
        return self._cards[position]

Object Lifecycle

Creation and Destruction

class Resource:
    def __new__(cls, *args, **kwargs):
        instance = super().__new__(cls)
        print("Creating new instance")
        return instance

    def __init__(self, name):
        self.name = name
        print(f"Initializing {name}")

    def __del__(self):
        print(f"Cleaning up {self.name}")

Context Management

class DatabaseConnection:
    def __enter__(self):
        print("Opening connection")
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print("Closing connection")
        if exc_type:
            print(f"Error occurred: {exc_val}")
            return False
        return True

Attribute Access

Basic Attribute Access

class LazyProperty:
    def __init__(self, function):
        self.function = function
        self.name = function.__name__

    def __get__(self, obj, type=None):
        if obj is None:
            return self
        value = self.function(obj)
        setattr(obj, self.name, value)
        return value

Custom Attribute Access

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

    def __get__(self, instance, owner):
        if instance is None:
            return self
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        if not isinstance(value, (int, float)):
            raise TypeError("Value must be numeric")
        instance.__dict__[self.name] = value

Container Types

Sequence Types

class Sequence:
    def __init__(self, items):
        self._items = list(items)

    def __len__(self):
        return len(self._items)

    def __getitem__(self, index):
        return self._items[index]

    def __setitem__(self, index, value):
        self._items[index] = value

    def __delitem__(self, index):
        del self._items[index]

Mapping Types

class DictLike:
    def __init__(self):
        self._data = {}

    def __getitem__(self, key):
        return self._data[key]

    def __setitem__(self, key, value):
        self._data[key] = value

    def __delitem__(self, key):
        del self._data[key]

    def __contains__(self, key):
        return key in self._data

Numeric Types

Basic Numeric Operations

class Number:
    def __init__(self, value):
        self.value = value

    def __add__(self, other):
        return Number(self.value + other.value)

    def __sub__(self, other):
        return Number(self.value - other.value)

    def __mul__(self, other):
        return Number(self.value * other.value)

    def __truediv__(self, other):
        return Number(self.value / other.value)

Comparison Operations

class Comparable:
    def __init__(self, value):
        self.value = value

    def __eq__(self, other):
        return self.value == other.value

    def __lt__(self, other):
        return self.value < other.value

    def __le__(self, other):
        return self.value <= other.value

Context Managers

Synchronous Context Managers

class FileManager:
    def __init__(self, filename, mode):
        self.filename = filename
        self.mode = mode
        self.file = None

    def __enter__(self):
        self.file = open(self.filename, self.mode)
        return self.file

    def __exit__(self, exc_type, exc_val, exc_tb):
        if self.file:
            self.file.close()

Asynchronous Context Managers

class AsyncResource:
    async def __aenter__(self):
        print("Acquiring resource")
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        print("Releasing resource")
        if exc_type:
            print(f"Error occurred: {exc_val}")
            return False
        return True

Best Practices

  1. Special Method Usage
  2. Use special methods for language integration
  3. Follow Python's conventions
  4. Document behavior clearly

  5. Performance Considerations

  6. Optimize critical operations
  7. Use appropriate data structures
  8. Consider memory usage

  9. Error Handling

  10. Raise appropriate exceptions
  11. Provide clear error messages
  12. Handle edge cases

  13. Code Organization

  14. Group related special methods
  15. Maintain consistent style
  16. Use type hints

  17. Classes

  18. Descriptors
  19. Iterators
  20. Metaclasses