Skip to content

✨ Python Dunder Methods Guide

Beginner-friendly documentation about Python dunder methods (magic methods), special object behavior, and common object-oriented patterns.


📚 Table of Contents


📖 What are Dunder Methods?

Dunder methods are: - special Python methods - surrounded by double underscores

Example:

__init__

The word:

dunder

means:

double underscore

Why Dunder Methods Exist

They allow Python objects to: - behave like built-in types - customize operators - customize printing - customize comparisons - support iteration


🚀 init

__init__ runs automatically when an object is created.

Used for: - initializing attributes - preparing object state


Example

class Player:

    def __init__(self, name):
        self.name = name

Creating the Object

player = Player("Sara")

🖨️ str

__str__ controls: - readable object printing


Example

class Player:

    def __init__(self, name):
        self.name = name

    def __str__(self):
        return f"Player({self.name})"

Using str

player = Player("Sara")

print(player)

Output:

Player(Sara)

Why str is Useful

Without __str__, Python prints:

<object at 0x123456>

which is not very readable.


🔍 repr

__repr__ provides: - developer/debug representation


Example

class Enemy:

    def __repr__(self):
        return "Enemy()"

Difference Between str and repr

Method Purpose
__str__ User-friendly output
__repr__ Developer/debug output

📏 len

__len__ controls: - object length behavior


Example

class Inventory:

    def __init__(self):
        self.items = ["Sword", "Potion"]

    def __len__(self):
        return len(self.items)

Using len()

inventory = Inventory()

print(len(inventory))

Output:

2

⚖️ eq

__eq__ controls: - equality comparison


Example

class Player:

    def __init__(self, score):
        self.score = score

    def __eq__(self, other):
        return self.score == other.score

Using Equality

player1 = Player(10)
player2 = Player(10)

print(player1 == player2)

Output:

True

add

__add__ customizes: - the + operator


Example

class Coins:

    def __init__(self, amount):
        self.amount = amount

    def __add__(self, other):
        return Coins(self.amount + other.amount)

Using +

c1 = Coins(5)
c2 = Coins(10)

result = c1 + c2

📦 getitem

__getitem__ allows: - indexing with []


Example

class Inventory:

    def __init__(self):
        self.items = ["Sword", "Potion"]

    def __getitem__(self, index):
        return self.items[index]

Using Indexing

inventory = Inventory()

print(inventory[0])

Output:

Sword

🔄 iter

__iter__ allows: - iteration with loops


Example

class Inventory:

    def __init__(self):
        self.items = ["Sword", "Potion"]

    def __iter__(self):
        return iter(self.items)

Using Iteration

for item in inventory:
    print(item)

🎮 Real 42 Examples

Maze String Representation

def __str__(self):

Useful for: - ASCII maze printing


Position Comparison

def __eq__(self, other):

Useful for: - coordinate comparison - pathfinding


Inventory Length

def __len__(self):

Useful for: - item systems


Custom Containers

def __getitem__(self, index):

Useful for: - grid systems - tile access


⚠️ Common Beginner Mistakes

❌ Returning Wrong Types

Bad:

def __len__(self):
    return "five"

__len__ must return: - integer


❌ Forgetting other in eq

Wrong:

def __eq__(self):

Correct:

def __eq__(self, other):

❌ Overcomplicating Dunder Methods

Keep behavior: - simple - predictable - readable


❌ Forgetting Return Values

Example:

def __str__(self):
    print("Player")

Wrong because: - __str__ must RETURN a string


📚 Best Practices

  • Keep dunder methods simple
  • Return correct types
  • Use dunder methods only when useful
  • Avoid unnecessary operator overloads
  • Focus on readability

🧠 Why Dunder Methods Matter

Dunder methods help objects: - behave naturally - integrate with Python syntax - support built-in operations - feel more flexible

They are heavily used in: - frameworks - libraries - games - APIs - custom containers


📚 Final Notes

Dunder methods are one of the most powerful features of Python OOP.

Understanding them helps create: - cleaner APIs - more intuitive objects - reusable systems - better project architecture

They become increasingly useful in larger and more advanced Python projects.