Python dunder methods, often called “magic methods,” are at the heart of Python’s object-oriented programming. The term “dunder” comes from “double underscore,” which these methods use as both prefixes and suffixes (e.g., __init__
, __str__
). These special methods let you define the behavior of objects in various contexts, from initialization to representation to mathematical operations.
Understanding and leveraging dunder methods can help you write more expressive and Pythonic code. To appreciate their utility, let’s explore their relevance to Python objects, specific examples like __str__
and __repr__
, and how to implement custom dunder methods.
Python Objects and Dunder Methods
At its core, Python treats everything as an object, whether it’s a number, a string, or a custom class.
Each object is an instance of some class, and classes can define dunder methods to customize object behavior. When you use operators like +
, access an attribute, or print an object, Python internally calls these dunder methods.
For example:
class Sample:
def __init__(self, value):
self.value = value
def __add__(self, other):
return self.value + other.value
a = Sample(10)
b = Sample(20)
print(a + b) # Internally calls a.__add__(b)
Here, the __add__
method lets you define what happens when you use the +
operator with two Sample
objects. Without this method, Python would raise a TypeError
since the +
operator wouldn’t know how to handle Sample
.
__str__
and __repr__
: String Representation of Objects
Two important dunder methods, __str__
and __repr__
, control how objects are represented as strings.
__repr__
: Provides a detailed, often unambiguous, string representation. It’s aimed at developers.__str__
: Provides a user-friendly representation. If__str__
isn’t defined, Python falls back to__repr__
.
Let’s look at an example:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __repr__(self):
return f"Person(name='{self.name}', age={self.age})"
def __str__(self):
return f"{self.name}, {self.age} years old"
person = Person("Alice", 30)
print(repr(person)) # Calls __repr__
print(str(person)) # Calls __str__
Explanation:
__repr__
is for developers. It provides enough detail to recreate the object if needed.__str__
is for end-users. It gives a human-readable summary.
If you only define __repr__
, calling str()
or using print()
will default to it.
Writing Custom Dunder Methods
Custom dunder methods allow you to tailor the behavior of your objects. They enhance flexibility and make objects behave like native Python types. Here’s how to write and use some common dunder methods:
Example: Emulating Arithmetic Operations
Suppose we have a class Vector
that represents 2D vectors. We want to add and compare vectors.
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 __eq__(self, other):
return self.x == other.x and self.y == other.y
def __repr__(self):
return f"Vector(x={self.x}, y={self.y})"
# Creating vectors
v1 = Vector(3, 4)
v2 = Vector(1, 2)
v3 = v1 + v2 # Calls __add__
print(v3) # Calls __repr__
print(v1 == v2) # Calls __eq__
Explanation:
__add__
enables the+
operator for vectors.__eq__
defines equality comparison using==
.__repr__
provides a developer-friendly representation.
Without these methods, adding or comparing vectors would require verbose, manual implementations.
Example: Defining Object Length
You can use __len__
to define how Python computes the length of your object.
class ShoppingCart:
def __init__(self):
self.items = []
def add_item(self, item):
self.items.append(item)
def __len__(self):
return len(self.items)
cart = ShoppingCart()
cart.add_item("Apples")
cart.add_item("Oranges")
print(len(cart)) # Calls __len__
Explanation:
__len__
makesShoppingCart
compatible with Python’slen()
function.- The method simplifies integration with Python’s standard tools.
Guidelines for Writing Dunder Methods
- Understand the Context: Use dunder methods to align objects with their intended purpose. For instance, if an object needs to act like a number, implement
__add__
,__sub__
, and similar methods. - Avoid Overuse: Overloading many dunder methods can make code harder to understand. Use them when they genuinely simplify usage or improve clarity.
- Balance
__str__
and__repr__
: Always implement__repr__
. Define__str__
for user-facing representations. - Follow Pythonic Practices: Write dunder methods to make objects intuitive and easy to use in their expected context.
Thank you for reading this article. I hope you found it helpful and informative. If you have any questions, or if you would like to suggest new Python code examples or topics for future tutorials, please feel free to reach out. Your feedback and suggestions are always welcome!
Happy coding!
C. C. Python Programming
You can also find this article at Medium.com