
Python
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.
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}")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) # TrueImplement 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)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])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| Concept | Remember |
|---|---|
| Descriptor | Control 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_ordering | Decorator to generate missing comparison methods |
| Advanced Customization | Magic methods enable deep Python integration |
You've mastered advanced OOP! Continue learning or apply these patterns to real projects.
Ready to practice? Try challenges or take the quiz
Resources
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