Q1When result = a + b runs, which method does Python call behind the scenes?
Special Methods — Teaching Your Class How + and print Behave
Learn Python's special methods (dunder methods). Customize + with __add__, control print output with __str__, and redefine == with __eq__ — all with diagrams.
Last time we sorted out instance, class, and static methods. Now we'll cover a different kind — special methods (a.k.a. dunder methods).
What Are Special Methods? — "Dunder Methods"
Methods like __init__ and __str__ — names wrapped in two underscores on each side — are called special methods. Because they sit between double underscores, they're also called dunder methods.
The key thing about special methods is that you don't call them directly. Python calls them automatically when you perform certain operations. Write v1 + v2 and Python calls v1.__add__(v2) under the hood. Write print(p) and p.__str__() is called. Write a == b and a.__eq__(b) runs.
+, ==, and friends.__add__ — Defining the + Operator
Take + as the obvious example. Suppose you have a Money class for amounts in yen, and you want Money(300) + Money(500) to give you Money(800). Try to add them out of the box and Python will throw a TypeError complaining it doesn't know how to add Money to Money.
The way to teach Python how to add them is the __add__ method. Define def __add__(self, other): and return a new instance built from self (the left side) and other (the right side). Now + works on your class.
class Money:
def __init__(self, amount):
self.amount = amount
def __add__(self, other): # called when you write +
return Money(self.amount + other.amount)
wallet = Money(300)
payment = Money(500)
total = wallet + payment # really wallet.__add__(payment)
print(total.amount) # 800
+ and Python calls self.__add__(other). self is the left operand, other is the right one. Whatever new instance you return becomes the result of +.__str__ and __repr__ — Two Kinds of String Representation
Next up: the special methods called when an instance gets converted to a string. There are two of them, with different goals.
- __str__ — called by print(p) and str(p). A user-friendly, readable string.
- __repr__ — called in the REPL or for debugging. A code-like, detailed string.
With no overrides, print(user) shows something like <__main__.User object at 0x...> — pretty useless. Define __str__ for a clean display, and define __repr__ too so debugging shows you the type and the contents at a glance.
class User:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self): # for print() / str()
return f"{self.name} ({self.age} years old)"
def __repr__(self): # for debugging
return f"User(name={self.name!r}, age={self.age})"
u = User("Alice", 30)
print(u) # Alice (30 years old) <- __str__
print(repr(u)) # User(name='Alice', age=30) <- __repr__
- Called by
print(u)andstr(u) - Goal: a string for end users to read
- Example:
Alice (30 years old)
- Called by
repr(u)and the interactive REPL - Falls back to here when __str__ is missing during print
- Goal: a debug string showing type and contents
- Example:
User(name='Alice', age=30)
__repr__ — it makes debugging much easier.__eq__ — Defining ==
Now equality. When you write a == b, Python calls a.__eq__(b) internally. If your class doesn't define __eq__, the default is "are they the same object in memory?" (an id comparison).
Say you have a Coupon class and you want two coupons with the same code to count as the same coupon — even if the discount values differ. That's a "business-level" equality, and __eq__ is where you spell it out.
== is an id check — same contents but separate instances still come out False. With __eq__, you can compare by content (code).class Coupon:
def __init__(self, code, discount):
self.code = code
self.discount = discount
def __eq__(self, other):
return self.code == other.code # same code = same coupon
c1 = Coupon("SPRING10", 0.10)
c2 = Coupon("SPRING10", 0.20) # different discount, same code
c3 = Coupon("SUMMER15", 0.15)
print(c1 == c2) # True (codes match)
print(c1 == c3) # False (codes differ)
What Happens Without __eq__?
If you don't define __eq__, c1 == c2 checks whether they're the same object in memory (literally pointing to the same box). Even if every attribute matches perfectly, two separately constructed instances live at different memory addresses and the result is False. Whenever you want "equal by contents," define __eq__ yourself.
Other Common Special Methods — __call__ / __len__ / __bool__
Beyond the four we've covered, several other special methods come up often. Three worth knowing right away:
- __call__ — makes an instance callable like a function (obj(...) syntax)
- __len__ — defines what len(obj) returns
- __bool__ — defines how the instance evaluates as a truth value in if obj: or bool(obj)
Each one teaches a built-in operation — function call, len(), the if truthiness check — to your custom class. A Logger that accumulates log entries is a great example to put all three on one class.
class Logger:
def __init__(self, name):
self.name = name
self.log = []
def __call__(self, message): # logger("...") works
self.log.append(message)
return f"[{self.name}] {message}"
def __len__(self): # for len(logger)
return len(self.log)
def __bool__(self): # for if logger:
return len(self.log) > 0
app = Logger("app")
print(app("App started")) # [app] App started
print(app("Login successful")) # [app] Login successful
print(len(app)) # 2
if app:
print("There are logs") # There are logs
logger(...), len(logger), if logger: — is taught to your custom class through three matching dunders.What If You Don't Define __bool__?
When __bool__ is missing, Python falls back to __len__. A length of 0 becomes False; anything else becomes True. If both are missing, instances are always truthy, no matter their state. That's the same rule that makes empty lists and empty strings evaluate to False.
There are plenty more — __hash__ (for use as a set/dict key), __lt__ / __gt__ (the < / > comparisons), __getitem__ (the obj[key] syntax), __iter__ (for x in obj: iteration), and others. You don't need to memorize them all. Just keep this mental map: "if you want to teach a built-in operation to your class, there's probably a matching dunder for it." Then look it up when you need it.
| Special method | Syntax | When it's called |
|---|---|---|
| __init__ | Money(300) | On instance creation |
| __add__ | a + b | + operator |
| __str__ | print(p) / str(p) | User-facing string |
| __repr__ | repr(p) / REPL display | Developer-facing string |
| __eq__ | a == b | Equality comparison |
| __call__ | obj(...) | Function-style call |
| __len__ | len(obj) | Length |
| __bool__ | if obj: / bool(obj) | Truthiness |
Knowledge Check
Answer each question one by one.
Q2Which description of __str__ and __repr__ is most accurate?
Q3If a class doesn't define __eq__, what does a == b compare? (Both are instances of the same class.)