Dunder Methods: Python's Magic Behind the Scenes
You keep seeing Python methods with weird double underscores like __init__
and __str__
, and people keep calling them “dunder” methods. What’s that about?
Why “Dunder”?
Simple: Double UNDERscore = “dunder”
Instead of saying “underscore underscore init underscore underscore” every time, Python folks just say “dunder init.” It’s way easier.
So __init__
becomes “dunder init”, __str__
becomes “dunder str”, and so on.
What Are Dunder Methods?
Dunder methods are Python’s way of letting you hook into the language’s built-in behavior. They’re like secret handshakes between your code and Python itself.
When you do something like len(my_list)
or str(my_object)
, Python is actually calling dunder methods behind the scenes.
The Most Common Ones
__init__
(Constructor)
This runs when you create a new object:
1class Person:
2 def __init__(self, name, age):
3 self.name = name
4 self.age = age
5
6# Python calls __init__ automatically
7person = Person("Alice", 30)
__str__
(String Representation)
This controls what happens when you print()
your object:
1class Person:
2 def __init__(self, name, age):
3 self.name = name
4 self.age = age
5
6 def __str__(self):
7 return f"{self.name} (age {self.age})"
8
9person = Person("Alice", 30)
10print(person) # Calls __str__ automatically
11# Output: Alice (age 30)
Without __str__
, you’d get something ugly like <__main__.Person object at 0x7f8b8c0d5f40>
.
__len__
(Length)
This makes len()
work on your objects:
1class Playlist:
2 def __init__(self):
3 self.songs = []
4
5 def add_song(self, song):
6 self.songs.append(song)
7
8 def __len__(self):
9 return len(self.songs)
10
11playlist = Playlist()
12playlist.add_song("Bohemian Rhapsody")
13playlist.add_song("Stairway to Heaven")
14
15print(len(playlist)) # Calls __len__ automatically
16# Output: 2
The Magic Happens Automatically
Here’s the cool part: you don’t call dunder methods directly. Python calls them for you when you use built-in functions and operators.
1class BankAccount:
2 def __init__(self, balance):
3 self.balance = balance
4
5 def __str__(self):
6 return f"Account balance: ${self.balance}"
7
8 def __add__(self, amount):
9 return BankAccount(self.balance + amount)
10
11 def __eq__(self, other):
12 return self.balance == other.balance
13
14account1 = BankAccount(100)
15account2 = BankAccount(50)
16
17# These operations call dunder methods automatically:
18print(account1) # __str__
19new_account = account1 + 25 # __add__
20are_equal = account1 == account2 # __eq__
A Few More Useful Ones
__repr__
: Developer-friendly string representation (used in debuggers)__eq__
: Equality comparison (==
)__lt__
: Less than comparison (<
)__add__
: Addition (+
)__getitem__
: Indexing (obj[key]
)__call__
: Makes your object callable like a function
Quick Example: Making a Custom List
1class SimpleList:
2 def __init__(self):
3 self.items = []
4
5 def add(self, item):
6 self.items.append(item)
7
8 def __len__(self):
9 return len(self.items)
10
11 def __getitem__(self, index):
12 return self.items[index]
13
14 def __str__(self):
15 return f"SimpleList({self.items})"
16
17# Now it behaves like a built-in list in some ways:
18my_list = SimpleList()
19my_list.add("apple")
20my_list.add("banana")
21
22print(len(my_list)) # 2 (calls __len__)
23print(my_list[0]) # "apple" (calls __getitem__)
24print(my_list) # SimpleList(['apple', 'banana']) (calls __str__)
Don’t Go Crazy
You don’t need to implement every dunder method. Only add the ones that make sense for your class. If your class doesn’t represent something that has a length, don’t implement __len__
.
Think about what operations would naturally make sense on your objects, then implement those dunder methods.
The Bottom Line
Dunder methods are Python’s way of letting your custom objects play nicely with built-in functions and operators. They’re called “magic methods” because they make your objects feel magical - they just work with Python’s built-in stuff automatically.
Start with __init__
and __str__
(you’ll use these constantly), then add others as you need them. Once you get comfortable with a few, you’ll start seeing opportunities to make your classes more intuitive and Pythonic.