Magic Methods in Python

Magic methods in Python, also known as dunder (double underscore) methods, are special methods that allow objects to interact with built-in operations like addition, comparison, string representation, and more. These methods start and end with double underscores, such as __init__, __str__, and __add__. Magic methods define how objects behave in different scenarios.

Commonly Used Magic Methods

MethodPurpose
__init__Initializes a new instance of a class.
__str__Returns a user-friendly string representation of an object.
__repr__Returns an official string representation of an object.
__len__Defines behavior for len() function.
__add__Defines behavior for + operator.
__eq__Defines behavior for == operator.

Examples

1. Using __init__ to Initialize Objects

The __init__ method is called automatically when a new object is created. It is used to set up the object’s attributes.

In this example, we define a class Person that takes a name and age as arguments. When we create a new Person object, the __init__ method assigns these values to the object’s attributes.

</>
Copy
class Person:
    def __init__(self, name, age):
        self.name = name  # Assign name to the object
        self.age = age  # Assign age to the object

# Creating an object of the Person class
p1 = Person("Arjun", 25)

# Printing object attributes
print("Name:", p1.name)
print("Age:", p1.age)

Output:

Name: Arjun
Age: 25

The __init__ method ensures that every time we create a Person object, it is initialized with a name and age.

2. Using __str__ to Define a Readable String Representation

The __str__ method allows us to define how an object is represented as a string when printed. Without this method, printing an object would return an unhelpful memory address.

Here, we modify our Person class to include __str__. This method returns a string describing the person.

</>
Copy
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

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

p1 = Person("Ram", 30)
print(p1)  # Calls __str__ method

Output:

Person(name=Ram, age=30)

Now, when we print the object, we get a readable representation instead of a generic memory address.

3. Using __add__ for Custom Addition

The __add__ method allows us to define how two objects should be added using the + operator.

In this example, we create a class Vector that represents a mathematical vector with an x and y component. By defining __add__, we allow two vectors to be added together using +.

</>
Copy
class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y)

    def __str__(self):
        return f"Vector({self.x}, {self.y})"

v1 = Vector(2, 3)
v2 = Vector(1, 4)

result = v1 + v2  # Calls __add__ method
print(result)

Output:

Vector(3, 7)

The __add__ method adds the x and y values of two vectors and returns a new Vector object.

4. Handling Errors When Using Magic Methods

If we try to add objects of different types, Python raises a TypeError. We can modify __add__ to handle this error.

</>
Copy
class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __add__(self, other):
        if not isinstance(other, Vector):
            raise TypeError("Can only add Vector to another Vector")
        return Vector(self.x + other.x, self.y + other.y)

v1 = Vector(2, 3)

try:
    result = v1 + 5  # Trying to add an integer
except TypeError as e:
    print("Error:", e)

Output:

Error: Can only add Vector to another Vector

We check if other is a Vector. If not, a TypeError is raised, preventing invalid operations.