__call__
This method makes an object behave like a function.
class Greeter:
def __call__(self, name):
return f"Hello, {name}!"
g = Greeter()
print(g("Amit"))
print(g("Sita"))
Output:
Hello, Amit!
Hello, Sita!
Learn how special double-underscore methods such as __init__,
__str__, __len__, __add__,
and __getitem__ allow your own classes to behave like natural Python objects.
In Python Object-Oriented Programming, magic methods are special methods whose names start and end with double underscores. They are also called dunder methods.
__init__
__str__
__repr__
__len__
__add__
__getitem__
We usually do not call these methods directly. Python calls them automatically when we perform normal actions.
| Normal action | Magic method Python calls |
|---|---|
print(obj) |
obj.__str__() |
len(obj) |
obj.__len__() |
obj1 + obj2 |
obj1.__add__(obj2) |
obj[index] |
obj.__getitem__(index) |
__init__: the object setup method
The __init__ method runs automatically when a new object is created.
It is used to store the first values inside an object.
class Student:
def __init__(self, name, marks):
self.name = name
self.marks = marks
s1 = Student("Amit", 85)
print(s1.name)
print(s1.marks)
Output:
Amit
85
Student("Amit", 85), Python automatically calls __init__.
__init__ is not the object itself. It is the method that prepares the object after it is created.
__str__ and __repr__: showing objects properly__str__class Student:
def __init__(self, name, marks):
self.name = name
self.marks = marks
s1 = Student("Amit", 85)
print(s1)
Output may look like this:
<__main__.Student object at 0x000001A2...>
This output is not useful for a student or a normal user.
__str__class Student:
def __init__(self, name, marks):
self.name = name
self.marks = marks
def __str__(self):
return f"{self.name} scored {self.marks} marks"
s1 = Student("Amit", 85)
print(s1)
Output:
Amit scored 85 marks
__repr__
__repr__ is generally used for programmer-friendly output.
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})"
s1 = Student("Amit", 85)
print(repr(s1))
Output:
Student(name='Amit', marks=85)
| Method | Purpose |
|---|---|
__str__ |
Friendly output for users |
__repr__ |
Clear output for programmers and debugging |
Operator overloading means giving special meaning to operators such as +, -, *, and > for our own classes.
class Money:
def __init__(self, amount):
self.amount = amount
m1 = Money(100)
m2 = Money(200)
print(m1 + m2)
Python does not automatically know how to add two Money objects.
So we define __add__.
__add__class Money:
def __init__(self, amount):
self.amount = amount
def __add__(self, other):
return Money(self.amount + other.amount)
def __str__(self):
return f"₹{self.amount}"
m1 = Money(100)
m2 = Money(250)
m3 = m1 + m2
print(m3)
Output:
₹350
m1 + m2, Python internally calls m1.__add__(m2).
| Operator | Magic method |
|---|---|
+ | __add__ |
- | __sub__ |
* | __mul__ |
/ | __truediv__ |
// | __floordiv__ |
% | __mod__ |
** | __pow__ |
class Box:
def __init__(self, chocolates):
self.chocolates = chocolates
def __add__(self, other):
return Box(self.chocolates + other.chocolates)
def __sub__(self, other):
return Box(self.chocolates - other.chocolates)
def __str__(self):
return f"Box has {self.chocolates} chocolates"
b1 = Box(20)
b2 = Box(5)
print(b1 + b2)
print(b1 - b2)
Output:
Box has 25 chocolates
Box has 15 chocolates
Comparison magic methods allow us to compare two objects using normal comparison operators.
| Comparison | Magic method |
|---|---|
== | __eq__ |
!= | __ne__ |
> | __gt__ |
< | __lt__ |
>= | __ge__ |
<= | __le__ |
class Student:
def __init__(self, name, marks):
self.name = name
self.marks = marks
def __gt__(self, other):
return self.marks > other.marks
def __eq__(self, other):
return self.marks == other.marks
s1 = Student("Amit", 85)
s2 = Student("Ravi", 90)
s3 = Student("Sita", 85)
print(s1 > s2)
print(s1 == s3)
Output:
False
True
Python lists, tuples, dictionaries, and strings support operations such as len(),
indexing, assignment by index, and in. Magic methods allow our own objects to support these actions too.
__len__: support for len()class Classroom:
def __init__(self, students):
self.students = students
def __len__(self):
return len(self.students)
c1 = Classroom(["Amit", "Ravi", "Sita"])
print(len(c1))
Output:
3
__getitem__: support for indexingclass Marksheet:
def __init__(self, marks):
self.marks = marks
def __getitem__(self, index):
return self.marks[index]
m = Marksheet([80, 75, 90, 88])
print(m[0])
print(m[2])
Output:
80
90
__setitem__: support for changing values by indexclass Marksheet:
def __init__(self, marks):
self.marks = marks
def __getitem__(self, index):
return self.marks[index]
def __setitem__(self, index, value):
self.marks[index] = value
m = Marksheet([80, 75, 90])
print(m[1])
m[1] = 95
print(m[1])
Output:
75
95
__contains__: support for inclass Classroom:
def __init__(self, students):
self.students = students
def __contains__(self, name):
return name in self.students
c = Classroom(["Amit", "Ravi", "Sita"])
print("Amit" in c)
print("Mohan" in c)
Output:
True
False
__call__This method makes an object behave like a function.
class Greeter:
def __call__(self, name):
return f"Hello, {name}!"
g = Greeter()
print(g("Amit"))
print(g("Sita"))
Output:
Hello, Amit!
Hello, Sita!
__bool__
This method controls truth testing with if object:.
class Wallet:
def __init__(self, money):
self.money = money
def __bool__(self):
return self.money > 0
w1 = Wallet(500)
w2 = Wallet(0)
print(bool(w1))
print(bool(w2))
Output:
True
False
__del__: object destruction
The method __del__ may run when an object is about to be destroyed.
However, in real projects we should not depend on its exact timing.
class Demo:
def __init__(self):
print("Object created")
def __del__(self):
print("Object destroyed")
d = Demo()
del d
__del__ as your main cleanup plan in serious programs.
Python controls memory automatically, and destruction time may not always be exactly when you expect.
This project shows how magic methods make our own class feel natural, like a normal Python container.
class ShoppingCart:
def __init__(self):
self.items = []
def add_item(self, item):
self.items.append(item)
def __len__(self):
return len(self.items)
def __contains__(self, item):
return item in self.items
def __getitem__(self, index):
return self.items[index]
def __str__(self):
return f"Cart contains: {self.items}"
cart = ShoppingCart()
cart.add_item("Laptop")
cart.add_item("Mouse")
cart.add_item("Keyboard")
print(cart)
print(len(cart))
print("Mouse" in cart)
print(cart[0])
Output:
Cart contains: ['Laptop', 'Mouse', 'Keyboard']
3
True
Laptop
print(cart), len(cart),
"Mouse" in cart, and cart[0].
Create a class called Book. It should have title and pages.
Use __str__ to print this:
Python Basics has 250 pages
Create a class called Box. It should store items.
Use __len__ so that len(box) works.
Create a class called Product. It should have name and price.
Use __gt__ to compare products by price.
Create a class called Wallet. It should have money.
Use __add__ to add two wallets.
w1 = Wallet(100)
w2 = Wallet(200)
print(w1 + w2)
Expected output:
300
Create a class called Marksheet that stores a list of marks.
Add __len__, __getitem__, and __setitem__.
Use the embedded editor below to run the examples.
Start by copying the ShoppingCart project, then modify it.
Magic methods are special double-underscore methods that allow your objects to behave like Python’s built-in objects.
| Action | Method Python looks for |
|---|---|
print(obj) | __str__ |
repr(obj) | __repr__ |
len(obj) | __len__ |
obj1 + obj2 | __add__ |
obj1 > obj2 | __gt__ |
obj[index] | __getitem__ |
obj[index] = value | __setitem__ |
value in obj | __contains__ |
obj() | __call__ |
if obj: | __bool__ |