- __init__() function
- *args and **kwargs
- Aliases
- and operator
- argparse
- Arrays
- asyncio
- Booleans
- Break statement
- Bytes
- Classes
- Closure
- Code blocks
- Comments
- Conditional statements
- Console
- Context manager
- Data class
- Data structures
- Data types
- Data visualization
- datetime module
- Decorator
- Dictionaries
- Dictionary comprehension
- Django
- Docstrings
- Dunder methods
- Encapsulation
- enum
- enumerate() function
- Equality operator
- Error handling
- Exception handling
- False
- File handling
- Filter()
- Flask framework
- Floats
- Floor division
- For loops
- Formatted strings
- Functions
- Generator
- Globals()
- Greater than operator
- Greater than or equal to operator
- If statement
- in operator
- Indices
- Inequality operator
- Inheritance
- Integers
- Iterator
- JSON
- Lambda function
- len() Function
- Less than operator
- Less than or equal to operator
- List append() method
- List comprehension
- List count()
- List insert() method
- List pop() method
- List reverse() method
- List sort() method
- Lists
- Logging
- map() function
- Match statement
- Math module
- Merge sort
- Min()
- Modules
- Modulo operator
- Multiline comment
- Multiprocessing
- Multithreading
- None
- not operator
- NumPy library
- OOP
- Optional arguments
- or operator
- Override method
- Package manager (pip)
- Packages
- Pandas library
- Parameters
- pathlib module
- Pickle
- Polymorphism
- print() function
- Property()
- Protocol
- Random module
- range() function
- Raw strings
- Recursion
- Reduce()
- Regular expressions
- requests Library
- return statement
- round() function
- Script
- Set comprehension
- Sets
- SQLite
- String decode()
- String find()
- String join() method
- String replace() method
- String split() method
- String strip()
- Strings
- Ternary operator
- time.sleep() function
- True
- try...except statement
- Tuples
- Type casting
- Type hints
- uuid module
- Variables
- Virtual environment
- What is Python?
- While loops
- yield
- Zip function
PYTHON
Python Dunder Methods: Syntax, Usage, and Examples
Python dunder methods are special methods with double underscores in their names, like __init__ and __str__. They let your objects work naturally with built-in Python behavior, such as printing, adding, comparing, and looping.
How to Use Dunder Methods in Python
Dunder methods live inside a class, and Python calls them automatically in specific situations. You can also call them manually, but most of the time you use the “normal” syntax and let Python do the rest.
Learn Python on Mimo
Basic syntax
Python
classPlaylist:
def__init__(self, name):
self.name = name
playlist = Playlist("Road Trip")
In this example, Python calls __init__ when you create a new object.
Example with a few common ones
Python
classBook:
def__init__(self, title, pages):
self.title = title
self.pages = pages
def__str__(self):
returnf"{self.title} ({self.pages} pages)"
book = Book("Clean Code",464)
print(book)
That print(book) line triggers __str__ behind the scenes. Without __str__, you’d get a less helpful output like <__main__.Book object at 0x...>.
Dunder methods map to Python operators
A lot of operators and built-in functions rely on dunder methods.
len(obj)callsobj.__len__()obj1 + obj2callsobj1.__add__(obj2)obj[key]callsobj.__getitem__(key)obj == othercallsobj.__eq__(other)
So, when you implement dunder methods, you’re basically teaching Python how your object should behave.
When to Use Dunder Methods
Dunder methods help most when you want your own classes to feel like built-in types. Here are a few common use cases.
1) When you want better string output for debugging
Printing an object should tell you something useful. Nobody wants to read memory addresses all day.
Adding __str__ and __repr__ makes your objects easier to inspect.
2) When you want custom objects to support operators
If your class represents something that has “math” behavior, like money, points, or measurements, operator support makes your code clean and readable.
Instead of writing:
Python
total = price.add(tax)
You can write:
Python
total = price + tax
3) When you want your objects to work with built-in functions
Built-ins like len(), iter(), sum(), sorted(), or membership checks (in) rely on dunder methods.
Adding the right methods lets your object work naturally in loops and conditions, like a list or dictionary.
4) When you want to control object creation and cleanup
Some dunder methods affect how objects are created and destroyed, like:
__new__for customizing instance creation__del__for cleanup (rarely needed, but it exists)
Most projects won’t need these, but it helps to know they exist.
Examples of Dunder Methods in Python
Let’s look at practical examples that show how these methods make your classes feel “Pythonic,” without any weird hacks.
Example 1: Making an object printable with str
Python
classEvent:
def__init__(self, title, location):
self.title = title
self.location = location
def__str__(self):
returnf"{self.title} in{self.location}"
event = Event("Meetup","Vienna")
print(event)
Output:
Meetup in Vienna
This is perfect for logs and quick prints.
Example 2: Using len so len() works
Python
classTodoList:
def__init__(self, tasks):
self.tasks = tasks
def__len__(self):
returnlen(self.tasks)
todo = TodoList(["Reply to emails","Review PR","Write notes"])
print(len(todo))# 3
Now your object fits right into code that expects something “countable.”
Example 3: Using add for custom + behavior
Imagine a points system for a learning app.
Python
classPoints:
def__init__(self, value):
self.value = value
def__add__(self, other):
return Points(self.value + other.value)
def__str__(self):
returnf"{self.value} points"
a = Points(50)
b = Points(25)
total = a + b
print(total)
Output:
75 points
That looks clean in code, and it still feels obvious what’s happening.
Example 4: Comparing objects with eq
Python
classUser:
def__init__(self, username):
self.username = username
def__eq__(self, other):
returnself.username == other.username
u1 = User("mira")
u2 = User("mira")
u3 = User("leon")
print(u1 == u2)# True
print(u1 == u3)# False
Now equality checks compare the actual data you care about, not memory addresses.
Example 5: Making your object iterable with iter
If you want to loop over your object like a list, implement __iter__.
Python
classShoppingCart:
def__init__(self, items):
self.items = items
def__iter__(self):
returniter(self.items)
cart = ShoppingCart(["bread","eggs","tea"])
for itemin cart:
print(item)
Output:
bread
eggs
tea
This also makes your object compatible with functions like list(cart) and sum(...) (depending on the items).
Example 6: Using getitem for indexing and [] access
Python
classTeam:
def__init__(self, members):
self.members = members
def__getitem__(self, index):
returnself.members[index]
team = Team(["Amina","Jonas","Sasha"])
print(team[0])# Amina
print(team[1])# Jonas
Now your object behaves like a sequence.
Learn More About Dunder Methods
There are a lot of dunder methods, so it helps to group them by what they do instead of trying to memorize them all like flashcards.
Common “display” methods: str and repr
__str__is for user-friendly output, usually forprint()__repr__is for developer-friendly output, often used in debugging
Python
classMovie:
def__init__(self, title, year):
self.title = title
self.year = year
def__str__(self):
returnf"{self.title} ({self.year})"
def__repr__(self):
returnf"Movie(title={self.title!r}, year={self.year})"
m = Movie("Spirited Away",2001)
print(m)# Spirited Away (2001)
print([m])# [Movie(title='Spirited Away', year=2001)]
That !r uses repr() formatting automatically, which is super handy for debugging.
Container behavior: len, contains, iter
Want your class to feel like a collection? These methods get you there.
__len__letslen(obj)work__contains__controlsvalue in obj__iter__makes your object loopable
Here’s a quick example using __contains__:
Python
classAllowedCountries:
def__init__(self, countries):
self.countries =set(countries)
def__contains__(self, item):
return item.lower()inself.countries
allowed = AllowedCountries(["montenegro","croatia","austria"])
print("croatia"in allowed)# True
print("france"in allowed)# False
Math and operators: add, sub, mul
Operator methods let you write simple code that reads nicely.
Python
classHours:
def__init__(self, value):
self.value = value
def__mul__(self, other):
return Hours(self.value * other)
def__str__(self):
returnf"{self.value}h"
work = Hours(2)
print(work *3)# 6h
You can also implement “reverse” operator methods like __radd__ so that 10 + obj works too, not just obj + 10.
Attribute access: getattr and setattr
These methods let you control what happens when someone reads or sets attributes. They can be powerful, but they’re also easy to mess up if you don’t keep the logic simple.
A small example:
Python
classConfig:
def__init__(self, values):
self.values = values
def__getattr__(self, name):
returnself.values.get(name)
cfg = Config({"theme":"dark","language":"en"})
print(cfg.theme)# dark
print(cfg.language)# en
print(cfg.missing)# None
Python calls __getattr__ only if the attribute doesn’t exist normally, which makes it safer than overriding everything.
Context managers: enter and exit
Context managers power the with statement. Files use this pattern:
Python
withopen("notes.txt")as f:
text = f.read()
You can build your own too, like a timer:
Python
import time
classTimer:
def__enter__(self):
self.start = time.time()
returnself
def__exit__(self, exc_type, exc_value, traceback):
end = time.time()
print(f"Time: {end - self.start:.3f}s")
with Timer():
total =sum(range(1_000_000))
This pattern is great for resource cleanup and for measuring performance without cluttering your code.
A quick note on init vs new
__new__creates the instance__init__initializes it
Most classes only need __init__. __new__ comes up when you subclass immutable types (like str or tuple) or when you need custom instance creation logic.
Common mistakes to avoid
1) Returning the wrong type
If __add__ returns a raw number sometimes and your class other times, your object becomes unpredictable. Try to return consistent types.
2) Forgetting to handle unsupported comparisons
Comparing to unrelated types can cause confusing bugs. Returning NotImplemented is often the right move.
Example:
Python
def__eq__(self, other):
ifnotisinstance(other, User):
returnNotImplemented
returnself.username == other.username
3) Going wild with magic
Dunder methods are powerful, but overusing them can make your code hard to follow. If a class behaves in surprising ways, someone will eventually call it “cursed” in a code review.
Summary
Dunder methods are special class methods that let your objects plug into Python’s built-in behavior, like printing, comparing, looping, indexing, and using operators. Add them when you want your custom classes to behave more like standard Python types, and keep the behavior clear so your code stays easy to read and maintain.
Join 35M+ people learning for free on Mimo
4.8 out of 5 across 1M+ reviews
Check us out on Apple AppStore, Google Play Store, and Trustpilot