Python Dunder Methods, Demystified 🪄
__init__, __repr__, __add__... they look scary with all those underscores. But once you get the one idea behind them, they stop being magic and start being obvious.
🔤 What Does "Dunder" Even Mean?
Dunder = Double UNDERscore.
__init__
↑ ↑
double double
underscore underscore
So dunder methods are just methods whose names start and end with __. You'll also hear them called magic methods or special methods — all three names mean the same thing.
Examples: __init__, __repr__, __eq__, __len__, __add__
💡 Why Do They Exist?
Python already knows what to do with its built-in types:
print(10 + 20) # 30
print(len("hello")) # 5
But now make your own class:
class Money:
def __init__(self, amount):
self.amount = amount
m1 = Money(100)
m2 = Money(200)
print(m1 + m2) # 💥 Error!
Python has no idea what + should mean for two Money objects. Dunder methods are how you teach Python what to do. Define __add__, and suddenly + makes sense for your class too.
The big idea: dunder methods let your custom objects behave like Python's built-in ones.
⚙️ Are They Automatic?
Yes — but you still write them yourself. You just don't call them directly.
| You write | Python actually calls |
|---|---|
a + b |
a.__add__(b) |
len(a) |
a.__len__() |
a == b |
a.__eq__(b) |
print(a) |
uses a.__repr__()
|
You define the dunder method once inside your class. From then on, Python quietly routes the normal syntax to it behind the scenes.
🧠 The translation trick: whenever you see a + b, mentally read it as a.__add__(b). Do this for every operator and dunder methods stop feeling magical.
🙋 A Quick Word on self
class Student:
def __init__(self, name):
self.name = name
s1 = Student("Deepika")
When Python runs s1 = Student("Deepika"), it's really doing s1.__init__("Deepika") internally — and inside that method, self is s1. So self.name = name simply means s1.name = "Deepika".
🧰 The Core Dunder Methods
__init__ — set up the object
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
Runs automatically when you create an object, to set its starting values.
⚠️ Common myth: "Every class must have
__init__." False — it's optional.class Student: passis perfectly valid Python.
__repr__ — give it a readable identity
Without it:
print(s)
# <__main__.Student object at 0x000001...>
With it:
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def __repr__(self):
return f"Student(name='{self.name}', age={self.age})"
print(s)
# Student(name='Deepika', age=21)
Mostly used for clean debugging/logging output.
__eq__ — define what "equal" means
class Student:
def __init__(self, name):
self.name = name
def __eq__(self, other):
return self.name == other.name
s1 = Student("Deepika")
s2 = Student("Deepika")
print(s1 == s2) # True
__len__ — teach len() about your object
class Team:
def __init__(self, members):
self.members = members
def __len__(self):
return len(self.members)
team = Team(["A", "B", "C"])
print(len(team)) # 3
__add__ — define what + means
class Money:
def __init__(self, amount):
self.amount = amount
def __add__(self, other):
return Money(self.amount + other.amount)
m3 = Money(100) + Money(200)
print(m3.amount) # 300
🧩 Putting It All Together
class Student:
def __init__(self, name, marks):
self.name = name
self.marks = marks
def __repr__(self):
return f"Student(name='{self.name}', marks={self.marks})"
def __eq__(self, other):
return self.marks == other.marks
def __len__(self):
return len(self.marks)
def __add__(self, other):
return Student(self.name + " & " + other.name,
self.marks + other.marks)
s1 = Student("Deepika", [90, 95])
s2 = Student("Koushik", [85, 92])
print(s1) # __repr__ → Student(name='Deepika', marks=[90, 95])
print(len(s1)) # __len__ → 2
print(s1 == s2) # __eq__ → False
s3 = s1 + s2 # __add__
🚨 Don't Confuse __main__ with __init__
if __name__ == "__main__":
print("Program started")
__name__ is a special variable and "__main__" is just a special value it can hold — it's not a dunder method. Easy to mix up because both have double underscores, but they're unrelated concepts.
🏢 Where This Shows Up in Real Code
| Scenario | Dunder used | Why |
|---|---|---|
| Combining prices in a cart | __add__ |
product1 + product2 reads naturally |
Financial apps (Money, Invoice) |
__add__ |
total = m1 + m2 instead of manual math |
| Shopping cart item count | __len__ |
len(cart) instead of len(cart.items)
|
Debugging a User object |
__repr__ |
print(user) → User(id=101, name='Deepika') instead of a memory address |
This isn't just interview trivia — it's how real Python libraries (like datetime, pandas, or pathlib) make objects feel native to the language.
🎯 Cheat-Sheet Answers
| Question | Short Answer |
|---|---|
| What are dunder methods? | Special methods with double-underscore names that define how your objects respond to built-in Python operations |
| Do we call them directly? | Usually no — normal syntax (+, len(), ==) triggers them automatically |
Do all classes need __init__? |
No, it's optional — only needed to initialize attributes |
| Why called "magic methods"? | Because Python invokes them behind the scenes, without an explicit call |
🧠 One Picture to Remember
YOUR OBJECT
│
"How should I behave?"
┌───────────┼────────────┐
↓ ↓ ↓
+ == len()
↓ ↓ ↓
__add__ __eq__ __len__
object created → __init__
object printed → __repr__
Golden rule: Dunder methods are the mechanism Python uses to let your custom objects participate naturally in its built-in operations.
Top comments (0)