Ojasa Mirai

Ojasa Mirai

Python

Loading...

Learning Level

🟢 Beginner🔵 Advanced
Classes & ObjectsMethods & SelfInstance VariablesClass VariablesConstructors & InitializationInheritance BasicsPolymorphismEncapsulationMagic Methods & Dunder
Python/Oop/Magic Methods Dunder

✨ Magic Methods & Dunder — Advanced Object Protocols

Advanced magic methods include the descriptor protocol, advanced comparison methods, numeric protocols, and context managers. These enable deep customization of object behavior at the Python level.


🎯 The Descriptor Protocol

Descriptors intercept attribute access with `__get__`, `__set__`, and `__delete__`.

class BoundedProperty:
    def __init__(self, min_val, max_val):
        self.min_val = min_val
        self.max_val = max_val
        self.name = None

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

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        return obj.__dict__.get(f"_{self.name}", None)

    def __set__(self, obj, value):
        if not (self.min_val <= value <= self.max_val):
            raise ValueError(f"{self.name} must be between {self.min_val} and {self.max_val}")
        obj.__dict__[f"_{self.name}"] = value

    def __delete__(self, obj):
        del obj.__dict__[f"_{self.name}"]

class Temperature:
    celsius = BoundedProperty(-273.15, 1000)

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

temp = Temperature(20)
print(temp.celsius)

try:
    temp.celsius = 2000  # Raises error
except ValueError as e:
    print(f"Error: {e}")

del temp.celsius
print(f"After delete: {temp.celsius}")

🔄 Numeric Protocol Methods

Implement advanced numeric operations and comparisons.

class Rational:
    """Represents a rational number (fraction)"""
    def __init__(self, numerator, denominator=1):
        if denominator == 0:
            raise ValueError("Denominator cannot be zero")
        self.num = numerator
        self.den = denominator
        self._simplify()

    def _simplify(self):
        from math import gcd
        g = gcd(abs(self.num), abs(self.den))
        self.num //= g
        self.den //= g

    def __add__(self, other):
        if isinstance(other, Rational):
            num = self.num * other.den + other.num * self.den
            den = self.den * other.den
        else:
            num = self.num + other * self.den
            den = self.den
        return Rational(num, den)

    def __radd__(self, other):
        return self.__add__(other)

    def __sub__(self, other):
        if isinstance(other, Rational):
            num = self.num * other.den - other.num * self.den
            den = self.den * other.den
        else:
            num = self.num - other * self.den
            den = self.den
        return Rational(num, den)

    def __mul__(self, other):
        if isinstance(other, Rational):
            return Rational(self.num * other.num, self.den * other.den)
        return Rational(self.num * other, self.den)

    def __truediv__(self, other):
        if isinstance(other, Rational):
            return Rational(self.num * other.den, self.den * other.num)
        return Rational(self.num, self.den * other)

    def __eq__(self, other):
        if isinstance(other, Rational):
            return self.num * other.den == other.num * self.den
        return self.num == other * self.den

    def __lt__(self, other):
        if isinstance(other, Rational):
            return self.num * other.den < other.num * self.den
        return self.num < other * self.den

    def __str__(self):
        return f"{self.num}/{self.den}"

    def __repr__(self):
        return f"Rational({self.num}, {self.den})"

r1 = Rational(1, 2)
r2 = Rational(1, 3)
print(r1 + r2)        # 5/6
print(r1 - r2)        # 1/6
print(r1 * r2)        # 1/6
print(r1 / r2)        # 3/2
print(r1 > r2)        # True

📋 Advanced Comparison and Hashing

Implement complete comparison and hashing protocols.

from functools import total_ordering

@total_ordering
class Version:
    """Semantic version comparable"""
    def __init__(self, major, minor=0, patch=0):
        self.major = major
        self.minor = minor
        self.patch = patch

    def __eq__(self, other):
        return (self.major, self.minor, self.patch) == (other.major, other.minor, other.patch)

    def __lt__(self, other):
        return (self.major, self.minor, self.patch) < (other.major, other.minor, other.patch)

    def __hash__(self):
        return hash((self.major, self.minor, self.patch))

    def __str__(self):
        return f"{self.major}.{self.minor}.{self.patch}"

    def __repr__(self):
        return f"Version({self.major}, {self.minor}, {self.patch})"

v1 = Version(1, 0, 0)
v2 = Version(1, 1, 0)
v3 = Version(1, 0, 0)

print(v1 == v3)       # True
print(v1 < v2)        # True
print(v1 <= v2)       # True (provided by @total_ordering)

# Can use in sets
versions = {v1, v2, v3}
print(len(versions))  # 2 (v1 and v3 are equal)

📚 Real-World Examples

Custom List-like Container

class CircularBuffer:
    """A fixed-size circular buffer"""
    def __init__(self, size):
        self._buffer = [None] * size
        self._size = size
        self._index = 0
        self._count = 0

    def __setitem__(self, index, value):
        if index >= self._size:
            raise IndexError("Index out of range")
        self._buffer[index] = value

    def __getitem__(self, index):
        if isinstance(index, slice):
            return [self._buffer[(self._index + i) % self._size]
                    for i in range(*index.indices(self._count))]
        return self._buffer[(self._index + index) % self._size]

    def __len__(self):
        return min(self._count, self._size)

    def __iter__(self):
        for i in range(min(self._count, self._size)):
            yield self._buffer[(self._index + i) % self._size]

    def append(self, value):
        self._buffer[self._index] = value
        self._index = (self._index + 1) % self._size
        self._count += 1

    def __str__(self):
        return f"CircularBuffer{list(self)}"

buffer = CircularBuffer(5)
for i in range(8):
    buffer.append(i)

print(buffer)           # CircularBuffer[3, 4, 5, 6, 7]
print(len(buffer))      # 5
print(buffer[0], buffer[1])

Lazy Evaluation Container

class LazySequence:
    """Sequence that computes values on demand"""
    def __init__(self, func, length):
        self.func = func
        self.length = length
        self._cache = {}

    def __getitem__(self, index):
        if isinstance(index, slice):
            return [self[i] for i in range(*index.indices(self.length))]
        if index not in self._cache:
            self._cache[index] = self.func(index)
        return self._cache[index]

    def __len__(self):
        return self.length

    def __iter__(self):
        for i in range(self.length):
            yield self[i]

    def __contains__(self, item):
        return any(self[i] == item for i in range(self.length))

# Sequence of squares computed on demand
squares = LazySequence(lambda x: x**2, 10)
print(squares[5])       # 25
print(list(squares))    # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
print(36 in squares)    # True

✅ Key Takeaways

ConceptRemember
DescriptorControl attribute access with __get__/__set__
Numeric Protocol__add__, __sub__, __mul__, __truediv__, etc.
Comparison__eq__, __lt__, __le__, __gt__, __ge__, __ne__
Hashing__hash__ for use in sets and dict keys
Container Protocol__getitem__, __setitem__, __len__, __iter__
Representation__str__ for users, __repr__ for developers
@total_orderingDecorator to generate missing comparison methods
Advanced CustomizationMagic methods enable deep Python integration

🔗 What's Next?

You've mastered advanced OOP! Continue learning or apply these patterns to real projects.

Back to OOP Overview →


Ready to practice? Try challenges or take the quiz


Resources

Python Docs

Ojasa Mirai

Master AI-powered development skills through structured learning, real projects, and verified credentials. Whether you're upskilling your team or launching your career, we deliver the skills companies actually need.

Learn Deep • Build Real • Verify Skills • Launch Forward

Courses

PythonFastapiReactJSCloud

© 2026 Ojasa Mirai. All rights reserved.

TwitterGitHubLinkedIn