Python Magic Methods
Magic methods in Python are also called “dunder” methods, which is short for “double underscores.” These are special methods with a specific naming convention: they start and end with double underscores, like _init_
, _str_
, and _add_
. These methods allow you to define behaviors for your classes that integrate seamlessly with Python’s built-in syntax and operations.
Magic methods should not be invoked directly. Instead, Python will internally invoke the magic method if you call certain things or use particular syntax. This is an in-depth listing of some common magic methods grouped by the purpose that each serves:
1. Initialization and Representation
Initialization: __init__
- Called when an instance of a class is created.
- Used to initialize the object’s attributes.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
p = Person("Alice", 30) # __init__ is called here
print(p.name) # Output: Alice
Representation: __str__
and __repr__
__str__
: Defines the string representation of an object for users (print
orstr()
).__repr__
: Defines the string representation for developers (repr()
or interactive shell).
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"Person: {self.name}, {self.age} years old"
def __repr__(self):
return f"Person(name='{self.name}', age={self.age})"
p = Person("Alice", 30)
print(p) # Output: Person: Alice, 30 years old (from __str__)
repr(p) # Output: Person(name='Alice', age=30) (from __repr__)
2. Arithmetic Operations
Binary Operators
__add__
: Implements+
__sub__
: Implements-
__mul__
: Implements*
__truediv__
: Implements/
__floordiv__
: Implements//
__mod__
: Implements%
__pow__
: Implements**
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __str__(self):
return f"Vector({self.x}, {self.y})"
v1 = Vector(2, 3)
v2 = Vector(4, 5)
v3 = v1 + v2 # __add__ is called
print(v3) # Output: Vector(6, 8)
Unary Operators
__neg__
: Implements unary-
(negation)__pos__
: Implements unary+
__abs__
: Implementsabs()
3. Comparison Operators
Comparison Methods
__eq__
: Implements==
__ne__
: Implements!=
__lt__
: Implements<
__le__
: Implements<=
__gt__
: Implements>
__ge__
: Implements>=
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __eq__(self, other):
return self.age == other.age
p1 = Person("Alice", 30)
p2 = Person("Bob", 30)
print(p1 == p2) # Output: True (calls __eq__)
4. Attribute Access
Custom Attribute Handling
getattr
: When the attribute is accessed, it is called.setattr
: When an attribute is assigned, it is called.delattr
: When an attribute is deleted, it is called.
class Person:
def __init__(self, name):
self.name = name
def __getattr__(self, attr):
return f"{attr} not found"
p = Person("Alice")
print(p.age) # Output: age not found (calls __getattr__)
5. Container Emulation
Methods for Collections
__len__
: Implementslen()
__getitem__
: Implements indexing (obj[key]
)__setitem__
: Implements assignment to indexed items (obj[key] = value
)__delitem__
: Implements item deletion (del obj[key]
)__contains__
: Implements membership test (in
)
class CustomList:
def __init__(self, data):
self.data = data
def __len__(self):
return len(self.data)
def __getitem__(self, index):
return self.data[index]
def __setitem__(self, index, value):
self.data[index] = value
my_list = CustomList([1, 2, 3])
print(len(my_list)) # Output: 3 (calls __len__)
print(my_list[1]) # Output: 2 (calls __getitem__)
my_list[1] = 42 # __setitem__ modifies the list
print(my_list[1]) # Output: 42
6. Callable Objects
__call__
- Makes an instance callable like a function.
class Adder:
def __init__(self, value):
self.value = value
def __call__(self, x):
return self.value + x
add_five = Adder(5)
print(add_five(10)) # Output: 15 (calls __call__)
7. Context Management
__enter__
and __exit__
- Implemented for use with
with
statements.
class FileManager:
def __init__(self, filename, mode):
self.file = open(filename, mode)
def __enter__(self):
return self.file
def __exit__(self, exc_type, exc_value, traceback):
self.file.close()
with FileManager("test.txt", "w") as f:
f.write("Hello, World!")
8. Miscellaneous
__iter__
: Makes an object iterable.__next__
: Implements iteration (next()
).__del__
: Defines destructor behavior (called when the object is deleted).
class Counter:
def __init__(self, low, high):
self.current = low
self.high = high
def __iter__(self):
return self
def __next__(self):
if self.current > self.high:
raise StopIteration
else:
self.current += 1
return self.current - 1
for num in Counter(1, 5):
print(num) # Output: 1, 2, 3, 4, 5
Why Use Magic Methods?
- Readable Code: They let your special classes interact smoothly with built-in syntax for things like arithmetic and iteration.
- Custom Behavior: Define how your objects behave in specific situations.
- Pythonic Design: Leverage the strengths of Python, such as duck typing and operator overloading.