__str__ and __repr__
__str__: human-readable string (for print). __repr__: developer representation (unambiguous, for debugging).
class Point:
def __init__(self, x, y):
self.x, self.y = x, y
def __str__(self):
return f"Point({self.x}, {self.y})"
def __repr__(self):
return f"Point(x={self.x!r}, y={self.y!r})"
p = Point(3, 4)
print(str(p)) # Point(3, 4) — uses __str__
print(repr(p)) # Point(x=3, y=4) — uses __repr__
print(p) # Point(3, 4) — print uses __str__Comparison Methods
Define how objects are compared with ==, <, >, <=, >= operators.
class Temperature:
def __init__(self, celsius):
self.celsius = celsius
def __eq__(self, other):
return self.celsius == other.celsius
def __lt__(self, other):
return self.celsius < other.celsius
def __le__(self, other):
return self.celsius <= other.celsius
t1 = Temperature(25)
t2 = Temperature(30)
print(t1 == t2) # False
print(t1 < t2) # True
temps = [Temperature(30), Temperature(10), Temperature(20)]
print([t.celsius for t in sorted(temps)]) # [10, 20, 30]Arithmetic Methods
Make + - * / work on your objects.
class Money:
def __init__(self, amount, currency="USD"):
self.amount = amount
self.currency = currency
def __add__(self, other):
if self.currency != other.currency:
raise ValueError("Cannot add different currencies")
return Money(self.amount + other.amount, self.currency)
def __str__(self):
return f"{self.currency} {self.amount:.2f}"
wallet = Money(50) + Money(30)
print(wallet) # USD 80.00Container Methods
__len__, __getitem__, __contains__ make custom objects behave like sequences.
class Library:
def __init__(self):
self._books = []
def add(self, book): self._books.append(book)
def __len__(self): return len(self._books)
def __getitem__(self, idx): return self._books[idx]
def __contains__(self, book): return book in self._books
lib = Library()
lib.add("Python Crash Course")
lib.add("Fluent Python")
print(len(lib)) # 2
print(lib[0]) # Python Crash Course
print("Fluent Python" in lib) # TrueDunder Methods: Making Your Objects Feel Built-in
"Magic" (dunder) methods let your class hook into Python's syntax. Define __add__ and + works on your objects; define __len__ and len() works. You're not calling them directly — the interpreter does.
class Money:
def __init__(self, amt): self.amt = amt
def __repr__(self): return f"Money({self.amt})" # for developers
def __str__(self): return f"${self.amt:.2f}" # for users
def __add__(self, other): return Money(self.amt + other.amt)
def __eq__(self, other): return self.amt == other.amt
print(Money(5) + Money(3)) # $8.00 — uses __add__ then __str__
Two pairings people get wrong
__str__vs__repr__:str()/printuse__str__(readable); the REPL and containers use__repr__(unambiguous, ideally reconstructable). Define__repr__at minimum — it's the fallback for both.__eq__+__hash__: define__eq__and Python sets__hash__toNone, making instances unhashable (can't go in sets/dict keys). If two equal objects should hash the same, define__hash__too.
🏋️ Practical Exercise
Create a Vector class representing a 2D vector:
- Implement
__init__(self, x, y). - Add
__repr__so the object prints asVector(2, 3). - Implement
__add__so two vectors can be added with+. - Implement
__eq__so two vectors with the same components compare equal.
🔥 Challenge Exercise
Build a Playlist class that wraps a list of songs and behaves like a container. Implement __len__, __getitem__ (so you can index and iterate it), __contains__ (so in works), and __str__ for a friendly printout. Demonstrate looping over the playlist with a for loop, checking membership, and printing its length.
📋 Summary
- Magic (dunder) methods have double underscores and let your objects integrate with Python’s built-in syntax.
__str__gives a readable string for users;__repr__gives an unambiguous string for developers.- Comparison methods (
__eq__,__lt__, …) enable operators like==and<. - Arithmetic methods (
__add__,__mul__, …) implement operator overloading. - Container methods (
__len__,__getitem__,__contains__) make objects behave like lists or dicts. - Implementing
__repr__aids debugging because it is what the REPL and containers display.
Interview Questions on Magic Methods
- What are magic (dunder) methods in Python?
- What is the difference between
__str__and__repr__? - Which magic methods let you use the
+and==operators on your objects? - How do you make a custom object work with
len()and theinoperator? - What magic methods make an object iterable?
- What does
__call__do? - Why is implementing
__repr__considered good practice?
Related Topics
FAQ
__str__ and __repr__? +__str__ is for an informal, readable representation shown by print() and str(). __repr__ is for an unambiguous, developer-facing representation shown in the REPL and by containers. If you only define one, define __repr__ — str() falls back to it.
Rarely. You usually trigger them through syntax: a + b calls a.__add__(b), len(x) calls x.__len__(). Python invokes them for you when you use the corresponding operator or built-in.
Defining magic methods like __add__ or __mul__ so standard operators work on your own objects. It lets a Vector + Vector or Money * 2 read naturally instead of needing named methods.
Implement __iter__ (returning an iterator) or, more simply, __getitem__ with integer indexes starting at 0. Either lets a for loop walk through your object.

