Special Dunder Methods
Implement Pythonic operator overloading, developer string representations with __repr__, and length checks.
Python's Data Model: A Secret Handshake With Built-ins
You never call .size() or .toString() on a Python object. Instead, len(obj), str(obj) and obj + other all silently dispatch to double-underscore "dunder" methods defined on the class: type(obj).__len__(obj), type(obj).__str__(obj), type(obj).__add__(obj, other).
Implement these protocols correctly and your custom class behaves indistinguishably from a built-in list, dict, or int.
raise TypeError(...) # crashes on 'x == 5'
if not isinstance(other, Vector2D):
return NotImplemented
Dunder methods are like standardized electrical outlets — Python's built-in functions are the appliances, and every object that implements the right "plug shape" (__len__, __add__) just works, no matter who built it.
Hooking into Python Protocols
Run the snippet below to see how str(), repr(), and list display each call a different dunder method:
class Money:
def __init__(self, amount, currency="USD"):
self.amount = round(float(amount), 2)
self.currency = currency
# Unambiguous developer representation
def __repr__(self):
return f"Money({self.amount}, '{self.currency}')"
# Human-friendly string formatting
def __str__(self):
return f"${self.amount:.2f} {self.currency}"
m = Money(49.95)
print("str(m):", str(m))
print("repr(m):", repr(m))
print("Interactive REPL output:", [m]) # Lists use repr() for items!Unambiguous debug representation; always implement this first since str() falls back to it.
Clean, readable text for print() and str() — the polished face of your object.
Must return a non-negative int; also powers automatic truthiness via len(obj) == 0.
The safe return value from a binary operator method when the other operand's type is unsupported.
See What Each Built-in Actually Calls
Click through repr, str, and operator dispatch to see the exact method Python invokes:
Written for developers: unambiguous, and ideally valid Python that could recreate the object. Implement this one first — str() falls back to it.
Representation, Sizing, and Operator Protocols
These three families cover nearly every dunder method you'll write in application code.
String representation
__repr__(self)Developer-facing, unambiguous. Used by REPL, debuggers, and containers displaying items (e.g. inside a list).
__str__(self)User-facing, readable. Used by str() and print(); falls back to __repr__ if not defined.
Sizing & truthiness
__len__(self) -> intMust return a non-negative integer. len() calls this directly; a negative value raises ValueError, a non-integer raises TypeError.
__bool__(self) -> boolControls truthiness directly. If absent, Python falls back to __len__() == 0 meaning False.
Operator overloading
__add__(self, other)Handles a + b. Return NotImplemented for incompatible types, never raise TypeError directly.
__eq__(self, other)Handles a == b. Same NotImplemented rule applies so Python can try the other operand's __eq__.
Worked Hands-on Walkthroughs
Progress step-by-step from basic string protocols through fallback behavior to full operator overloading:
Hooking into Python Protocols
print and str() invoke __str__, while container displays and interactive shells invoke __repr__.
class Money:
def __init__(self, amount, currency="USD"):
self.amount = round(float(amount), 2)
self.currency = currency
# Unambiguous developer representation
def __repr__(self):
return f"Money({self.amount}, '{self.currency}')"
# Human-friendly string formatting
def __str__(self):
return f"${self.amount:.2f} {self.currency}"
m = Money(49.95)
print("str(m):", str(m))
print("repr(m):", repr(m))
print("Interactive REPL output:", [m]) # Lists use repr() for items!print and str() invoke __str__, while container displays and interactive shells invoke __repr__.
Demonstrating Fallback Behavior and Unambiguous Output
Because __str__ was omitted, Python seamlessly routed str(node) to __repr__.
class ServerNode:
def __init__(self, hostname, ip):
self.hostname = hostname
self.ip = ip
def __repr__(self):
return f"ServerNode(hostname='{self.hostname}', ip='{self.ip}')"
node = ServerNode("web-01", "10.0.0.1")
# Fallback: __str__ is not defined, so str() uses __repr__()
print("str() fallback:", str(node))
print("repr():", repr(node))
print("Are str and repr identical here?", str(node) == repr(node))Because __str__ was omitted, Python seamlessly routed str(node) to __repr__.
Arithmetic Overloading with Rich Comparison and NotImplemented
Returning NotImplemented in __eq__ allows Python to compare against incompatible types gracefully, returning False instead of crashing.
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Vector2D({self.x}, {self.y})"
def __add__(self, other):
if not isinstance(other, Vector2D):
return NotImplemented # Let Python try other.__radd__ or raise TypeError
return Vector2D(self.x + other.x, self.y + other.y)
def __eq__(self, other):
if not isinstance(other, Vector2D):
return NotImplemented
return (self.x, self.y) == (other.x, other.y)
v1 = Vector2D(2, 5)
v2 = Vector2D(3, 1)
print("v1 + v2:", v1 + v2)
print("v1 == Vector2D(2, 5):", v1 == Vector2D(2, 5))
print("v1 == 'invalid':", v1 == "invalid") # Returns False gracefully!Returning NotImplemented in __eq__ allows Python to compare against incompatible types gracefully, returning False instead of crashing.
Common Traps & How to Avoid Them
Study the mistakes and clean fixes below before writing your own dunder methods:
# Mistyped variable or missing closing parenthesis
result = dunder_methods___magic_protocols_val# Declare and verify variables before execution
val = 10
print("Defined correctly:", val)Beginners frequently encounter typos or forgotten definitions before using an identifier.
# Modifying shared state or unexpected variable shadowing
def update_state(data=[]):
data.append(1)
return datadef update_state(data=None):
if data is None:
data = []
data.append(1)
return dataDefault arguments in Python are evaluated once at function definition time, not at invocation time.
# Opening resources in a tight loop without closing or bounding memory
files = [open(f"log_{i}.txt", "w") for i in range(5000)]for i in range(5):
with open(f"log_{i}.txt", "w") as f:
f.write("Safe")Failing to release system resources leads to process termination under heavy production workloads.
Real-World Systems in Action
See how scripts, cloud microservices, and tensor libraries all lean on Python's Data Model:
Local Developer Automation Tool
Individual developers and IT administrators automate everyday repetitive workflows.
A clean standalone script providing fast, local automation with zero dependencies.
class Book:
def __init__(self, title, pages):
self.title = title
self.pages = pages
def __len__(self):
return self.pages
book = Book("Fluent Python", 800)
print("Length of book:", len(book))Backend API Service Integration
SaaS platforms run backend microservices processing requests concurrently.
A modular service handler integrating validation and structured responses.
# Service integration layer
def handle_request(payload: dict) -> dict:
return {"status": "success", "processed": True}High-Throughput Cloud Architecture
Data science frameworks like PyTorch and NumPy overload arithmetic dunder methods (__add__, __matmul__) to enable tensor math.
Data science frameworks like PyTorch and NumPy overload arithmetic dunder methods (__add__, __matmul__) to enable tensor math.
# High-resilience production pattern
import time
def process_stream(events: list) -> int:
processed = 0
for event in events:
if event:
processed += 1
return processedKey Points & Concept Review
- Dunder methods hook your classes into Python's native syntactic operators and built-in functions.
- Always implement __repr__ first: it serves as the foundation for debugging and the fallback for str().
- __str__ provides human-friendly formatting for print(), while __repr__ provides unambiguous technical representation.
- __len__ must return a non-negative integer and serves as the automatic fallback for truth value testing.
- Return NotImplemented (never raise TypeError) from binary operators when operand types are incompatible.
- Classes implementing data model protocols act indistinguishably from built-in Python primitives.
Frequently Asked Questions & Reference Hub
Once __repr__ vs __str__ clicks, every other dunder method — __add__, __eq__, __getitem__ — follows the exact same "the operator dispatches to a method" pattern. You're not memorizing dozens of rules, just one idea applied dozens of times.
See exactly when each string protocol is invoked.
Understand how len() interacts with truth value testing.
Learn to implement arithmetic and comparison protocols.
See why returning NotImplemented is the correct pattern.
Learn to emulate sequences and mappings.
Compare hand-written dunders against @dataclass.
You've Mastered Python's Data Model!
You understand string representation protocols, sizing, and safe operator overloading. Now lock in your knowledge with hands-on practice.
Implement __add__ and __repr__ on a custom Point class.
Earn experience points toward your developer rank as soon as you pass.
Bite-sized knowledge check with instant explanations for every question.
Worked example
Provide custom string representations for user display.
class Coin:
def __str__(self): return "Gold Coin"
print(str(Coin()))
How it works, step by step
- __str__ provides human-friendly output for str() and print().
Special Dunder Methods practice
Create a 2D Point class with __init__(self, x, y). Implement __add__(self, other) so that adding two points returns a new Point(self.x + other.x, self.y + other.y), and implement __repr__(self) returning f"Point({self.x}, {self.y})". Add p1 = Point(1, 2) and p2 = Point(3, 4), and print: Point(4, 6).
Coding Practice is Locked
Sign in to access the interactive Python editor, write and run code checks against automated tests, use hints, and earn XP.
Practice quiz
Check yourself
15 questions with explanations after submission, so you can correct course as you go.
Special Dunder Methods quiz
LockedYou must be signed in to take this quiz. Sign in to test your understanding, track your answers, and earn XP.
Lesson incomplete
Not completedCreate an ID or sign in to save lesson completion across devices.