This repository contains a collection of self-contained examples demonstrating the use of Python's magic (or "dunder") methods. These methods allow you to customize the behavior of your classes so that your objects work seamlessly with Python’s built-in functions, operators, and other language features.
Python magic methods are special methods with names that begin and end with double underscores (e.g., __init__, __str__, __add__). They enable you to override and extend the behavior of your classes—making your objects support operations such as arithmetic, type conversion, comparison, string representation, and more. This repository provides clear, runnable examples for many categories of magic methods.
- Python 3.6 or higher (Python 3.8+ recommended)
- Basic familiarity with Python classes and object-oriented programming
This repository is organized into several sections. Each section includes example code that demonstrates how to implement and use a set of related magic methods.
These methods control the creation, initialization, and destruction of objects.
__new__(cls, ...)
Called first to create a new instance.__init__(self, ...)
Initializes the instance after creation.__del__(self)
Called when the object is about to be destroyed.
Example:
class MyClass:
def __new__(cls, value):
print("Calling __new__")
instance = super().__new__(cls)
return instance
def __init__(self, value):
print("Calling __init__")
self.value = value
def __del__(self):
print(f"Deleting instance with value: {self.value}")
# Create and delete an instance:
obj = MyClass(10)
print("Object value:", obj.value)
del objThese methods let your objects interact with numeric functions and operations (e.g., abs(), round(), bitwise inversion).
__trunc__(self): Called bymath.trunc()__ceil__(self): Called bymath.ceil()__floor__(self): Called bymath.floor()__round__(self, n): Called byround()__invert__(self): Called by the~operator__abs__(self): Called byabs()__neg__(self): Called by the unary-operator__pos__(self): Called by the unary+operator
Example:
import math
class Number:
def __init__(self, value):
self.value = value
def __trunc__(self):
print("Calling __trunc__")
return int(self.value)
def __ceil__(self):
print("Calling __ceil__")
return math.ceil(self.value)
def __floor__(self):
print("Calling __floor__")
return math.floor(self.value)
def __round__(self, n=0):
print("Calling __round__")
return round(self.value, n)
def __invert__(self):
print("Calling __invert__")
return ~int(self.value)
def __abs__(self):
print("Calling __abs__")
return abs(self.value)
def __neg__(self):
print("Calling __neg__")
return -self.value
def __pos__(self):
print("Calling __pos__")
return +self.value
n = Number(3.7)
print("trunc:", math.trunc(n))
print("ceil:", n.__ceil__())
print("floor:", n.__floor__())
print("round (1 decimal):", n.__round__(1))
print("invert (bitwise):", n.__invert__())
print("abs:", abs(n))
print("negated:", -n)
print("unary plus:", +n)These methods allow you to override standard arithmetic operations (e.g., +, -, *, /).
__add__(self, other): Addition (+)__sub__(self, other): Subtraction (-)__mul__(self, other): Multiplication (*)__floordiv__(self, other): Floor division (//)__truediv__(self, other): True division (/)__mod__(self, other): Modulus (%)__divmod__(self, other): Division and modulus viadivmod()__pow__(self, power): Exponentiation (**)__lshift__(self, other): Left bitwise shift (<<)__rshift__(self, other): Right bitwise shift (>>)__and__(self, other): Bitwise AND (&)__or__(self, other): Bitwise OR (|)__xor__(self, other): Bitwise XOR (^)
Example:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
if isinstance(other, Vector):
return Vector(self.x + other.x, self.y + other.y)
raise TypeError("Unsupported operand type(s) for +")
def __sub__(self, other):
if isinstance(other, Vector):
return Vector(self.x - other.x, self.y - other.y)
raise TypeError("Unsupported operand type(s) for -")
def __mul__(self, other):
if isinstance(other, (int, float)):
return Vector(self.x * other, self.y * other)
elif isinstance(other, Vector):
# Dot product
return self.x * other.x + self.y * other.y
raise TypeError("Unsupported operand type(s) for *")
# (Other methods omitted for brevity)
def __repr__(self):
return f"Vector({self.x}, {self.y})"
v1 = Vector(3, 4)
v2 = Vector(1, 2)
print("v1 + v2 =", v1 + v2)
print("v1 - v2 =", v1 - v2)
print("v1 * 3 =", v1 * 3)
print("v1 * v2 (dot product) =", v1 * v2)Customize how objects are converted to strings, hashed, and represented.
__str__(self): Called bystr()andprint()for a user-friendly representation.__repr__(self): Called byrepr()for a developer-friendly representation.__unicode__(self): (Mostly relevant in Python 2; in Python 3,__str__returns a Unicode string.)__format__(self, format_spec): Called by theformat()function.__hash__(self): Called byhash()__bool__(self): Called bybool()(renamed from__nonzero__in Python 3)__dir__(self): Returns a list of attributes (used bydir())__sizeof__(self): Returns the size in bytes (used bysys.getsizeof())
Example:
class CustomString:
def __init__(self, text):
self.text = text
def __str__(self):
return f"CustomString: {self.text}"
def __repr__(self):
return f"CustomString({self.text!r})"
def __unicode__(self):
return self.text
def __format__(self, format_spec):
return format(self.text, format_spec)
def __hash__(self):
return hash(self.text)
def __bool__(self):
return bool(self.text)
def __dir__(self):
return ['text']
def __sizeof__(self):
return len(self.text)
cs = CustomString("Hello, World!")
print("str:", str(cs))
print("repr:", repr(cs))
print("Formatted:", format(cs, "^20"))
print("Hash:", hash(cs))
print("Bool:", bool(cs))
print("dir:", dir(cs))
print("Size (approx):", cs.__sizeof__(), "bytes")These methods let you compare objects using operators such as ==, <, >, <=, and >=.
__eq__(self, other): Equality (==)__ne__(self, other): Inequality (!=)__lt__(self, other): Less than (<)__gt__(self, other): Greater than (>)__le__(self, other): Less than or equal to (<=)__ge__(self, other): Greater than or equal to (>=)
Example:
class Comparable:
def __init__(self, value):
self.value = value
def __eq__(self, other):
if isinstance(other, Comparable):
return self.value == other.value
return NotImplemented
def __ne__(self, other):
if isinstance(other, Comparable):
return self.value != other.value
return NotImplemented
def __lt__(self, other):
if isinstance(other, Comparable):
return self.value < other.value
return NotImplemented
def __gt__(self, other):
if isinstance(other, Comparable):
return self.value > other.value
return NotImplemented
def __le__(self, other):
if isinstance(other, Comparable):
return self.value <= other.value
return NotImplemented
def __ge__(self, other):
if isinstance(other, Comparable):
return self.value >= other.value
return NotImplemented
def __repr__(self):
return f"Comparable({self.value})"
a = Comparable(10)
b = Comparable(20)
print("a == b:", a == b)
print("a != b:", a != b)
print("a < b :", a < b)
print("a > b :", a > b)
print("a <= b:", a <= b)
print("a >= b:", a >= b)-
Clone the Repository
git clone https://github.com/yourusername/python-magic-methods.git cd python-magic-methods -
Run an Example
Each example is self-contained. For instance, to run the initialization example:python init_example.py
Alternatively, you can copy any code snippet into a new Python file and run it with:
python your_example.py
-
Experiment and Modify
Feel free to modify the examples or combine them in your projects to better understand how magic methods integrate with Python’s operations.
- Python Data Model (Official Documentation)
- Real Python: Python's Magic Methods
- GeeksforGeeks: Dunder Methods in Python
This project is provided for educational purposes. Feel free to use, modify, and distribute the code under the terms of the MIT License.
Happy Pythoning!