Sitemap
Saurabh Gandhi

Saurabh Gandhi

Python • AI • Tech Learning Writing simple guides that make coding easy for everyone.

Magic (Dunder) Methods in Python

4 min readApr 15, 2026

--

In Python’s object-oriented model, objects can interact with built-in language features such as print(), len(), comparison operators, and more. This interaction is enabled through special methods, commonly called Magic Methods or Dunder Methods (Double-UNDERscore methods).

These methods allow developers to define how objects behave when used with Python’s built-in operations.

For example:

  • print(obj) → calls __str__()
  • len(obj) → calls __len__()
  • obj1 == obj2 → calls __eq__()

Magic methods make custom objects behave like built-in Python types such as lists, strings, or dictionaries.

Understanding Magic (Dunder) Methods

Magic methods are predefined method names in Python that begin and end with double underscores.

Example:

__init__
__str__
__repr__
__len__
__eq__

These methods are not meant to be called directly. Instead, Python automatically invokes them when certain operations occur.

Example:

class Demo:
def __str__(self):
return "Demo Object"

d = Demo()
print(d)# internally becomes d.__str__()

Why Magic Methods Are Important

Magic methods allow developers to:

  • Customize object behavior
  • Integrate custom classes with Python’s built-in functions
  • Implement operator overloading
  • Build intuitive APIs
  • Make objects behave like built-in data types

Without magic methods, custom classes would be much less flexible.

1. __str__() — Human-Readable Representation

__str__() returns a human-friendly string representation of an object.

This method is automatically called by:

  • print(obj)
  • str(obj)
  • format(obj)

The goal of __str__() is to provide a clean and readable description of the object.

Syntax:

def __str__(self):
return "string representation"

Example:

class Student:

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

def __str__(self):
return f"Student(Name={self.name}, Age={self.age})"


s = Student("Alice", 22)
print(s)
# output:
# Student(Name=Alice, Age=22)

Without __str__()

If __str__() is not implemented:

print(s)
# Output:
# <__main__.Student object at 0x0000018F2D3>

This output is not useful for users.

2. __repr__() — Developer-Focused Representation

__repr__() returns the official string representation of an object intended for developers and debugging.

It is used by:

  • Python interpreter
  • Debugging tools
  • repr(obj)
  • Interactive shell

The representation should ideally allow recreating the object.

Get Saurabh Gandhi’s stories in your inbox

Join Medium for free to get updates from this writer.

Syntax:

def __repr__(self):
return "representation"

Example:

class Student:

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

def __repr__(self):
return f"Student('{self.name}', {self.age})"

s = Student("Alice", 22)
repr(s)
# output:
# Student('Alice', 22)

__repr__() vs __str__()

Press enter or click to view image in full size

Example:

class Product:

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

def __str__(self):
return f"{self.name} costs ₹{self.price}"

def __repr__(self):
return f"Product('{self.name}', {self.price})"


p = Product("Laptop", 75000)

print(p)
repr(p)

3. __len__() — Defining Object Length

__len__() allows an object to define its length.

It is invoked when calling:

len(object)

This method must return a non-negative integer.

Example:

class Library:

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

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


lib = Library(["Python", "ML", "AI"])

print(len(lib))

Practical Example — Shopping Cart

class ShoppingCart:

def __init__(self):
self.items = []

def add_item(self, item):
self.items.append(item)

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


cart = ShoppingCart()

cart.add_item("Laptop")
cart.add_item("Mouse")

print(len(cart))

4. __del__() — Object Destructor

__del__() is a destructor method called when an object is about to be destroyed.

It is used for:

  • releasing resources
  • closing files
  • cleaning up connections

Syntax:

def __del__(self):
# cleanup code

Example:

class FileHandler:

def __init__(self, filename):
self.file = open(filename, "w")

def __del__(self):
print("Closing file")
self.file.close()


f = FileHandler("test.txt")
del f

Important Concept: Garbage Collection

Python uses reference counting and garbage collection to destroy objects.

Therefore, __del__() execution time is not always predictable.

This means:

__del__() is rarely used in production systems

Instead developers use context managers (with statement).

5. __eq__() — Custom Equality Comparison

__eq__() defines how two objects should be compared using ==.

Syntax:

def __eq__(self, other):

Example:

class Employee:

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

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


e1 = Employee(101)
e2 = Employee(101)

print(e1 == e2)

Default Behavior Without __eq__

Without defining __eq__, Python compares memory addresses.

Example:

class Test:
pass

a = Test()
b = Test()

print(a == b)

Execution Flow of Magic Methods

User Operation
|
v
Python Built-in Function
|
v
Magic Method Triggered

Example:

print(obj)      → obj.__str__()
repr(obj) → obj.__repr__()
len(obj) → obj.__len__()
del obj → obj.__del__()
obj1 == obj2 → obj.__eq__()

Questions

  1. What are magic methods in Python?
  2. Why are they called “dunder methods”?
  3. What is the purpose of __str__()?
  4. Difference between __str__() and __repr__()?
  5. When does Python call __len__()?
  6. What happens if __eq__() is not implemented?

--

--