Python Magic Methods: A Beginner’s Guide to __str__

Have you ever created a custom class in Python, made an object from it, and tried to print it—only to be greeted by a cryptic message like <__main__.Date object at 0x0000012345>? If so, you’ve encountered a common beginner frustration. Your objects just aren’t “print-friendly” like Python’s built-in strings or integers.

But what if you could make your custom objects just as easy to print? What if you could define exactly what should be displayed when someone passes your object to the print() function? This is where Python’s elegant solution, Magic Methods, comes into play.

In this guide, you’ll learn about one of the most useful magic methods: __str__. By the end, you’ll be able to make any of your custom classes cleanly and automatically printable, making your code more professional and user-friendly.

What Are Python Magic Methods?

Let’s demystify the name first. Python Magic Methods are special methods that let you add “magical” behaviors to your classes. They are also called Dunder Methods because they are surrounded by double underscores (Dunder = Double UNDERscore).

You’ve already used one without even realizing it! The __init__ method, which you use to construct new objects, is a magic method.

class Date:
    def __init__(self, dd, mm, yyyy):
        self.dd = dd
        self.mm = mm
        self.yyyy = yyyy

# __init__ is automatically called here!
my_date = Date(15, 8, 2025)

Each magic method has a specific duty. The duty of __init__ is to initialize a new object. Today, we’re focusing on the duty of another magic method: __str__.

The Common Problem: Making Objects Printable

Think about the built-in data types. Printing them is straightforward and gives you a clear, human-readable output.

a_string = "Hello World"
an_integer = 42

print(a_string)  # Output: Hello World
print(an_integer) # Output: 42

Now, let’s try to print an object from our simple Date class.

my_date = Date(15, 8, 2025)
print(my_date)  # Output: <__main__.Date object at 0x1093b7d90>

That output is not helpful! It’s the default way Python represents an object when it doesn’t know how to convert it to a string. To get a useful output, we often end up creating a custom method like display().

class Date:
    def __init__(self, dd, mm, yyyy):
        self.dd = dd
        self.mm = mm
        self.yyyy = yyyy

    def display(self):
        print(f"{self.dd}/{self.mm}/{self.yyyy}")

my_date = Date(15, 8, 2025)
my_date.display()  # Output: 15/8/2025

This works, but it’s manual. Wouldn’t it be better if our object was naturally compatible with print(), just like a string or an integer? This is the problem the __str__ magic method solves.

The Magical Solution: The __str__ Method

Python provides the __str__ method to define a “nice” string representation of your object. When you pass an object to the print() function, Python automatically looks for the __str__ method inside the object’s class. If it finds it, it uses the string that this method returns.

Here’s how we implement it in our Date class:

class Date:
    def __init__(self, dd, mm, yyyy):
        self.dd = dd
        self.mm = mm
        self.yyyy = yyyy

    def __str__(self):
        # Return a formatted string
        return f"{self.dd}/{self.mm}/{self.yyyy}"

# Create a Date object
my_date = Date(15, 8, 2025)

# Now, print() automatically calls __str__!
print(my_date)  # Output: 15/8/2025

The magic has happened! By simply defining __str__, we’ve made our Date object directly printable. Notice that we return a string from the method; we don’t print it inside the method. print() handles the printing for us.

A More Complex Example

Let’s see this in action with a more complex class, like an OnlineMeeting.

class OnlineMeeting:
    def __init__(self, meeting_id, date):
        self.id = meeting_id
        self.date = date  # 'date' is a Date object

    def __str__(self):
        # We can format the string any way we want.
        return f"Meeting ID: {self.id}\nDate of Meeting: {self.date}"

# Create objects
meeting_date = Date(15, 8, 2025)
om1 = OnlineMeeting(1234, meeting_date)

# Printing the OnlineMeeting object automatically uses its __str__
print(om1)

Output:

Meeting ID: 1234
Date of Meeting: 15/8/2025

Here’s the real beauty: when we use {self.date} inside the OnlineMeeting‘s __str__ method, Python automatically calls the Date class’s __str__ method to get its string representation. The magic methods are working together seamlessly!

With __str__ implemented, we can completely remove the old display() method. Our classes are now cleaner and more intuitive.

Ready to Go from Basics to Pro?

Understanding magic methods is a significant leap in your Python journey, opening the door to more advanced and powerful programming techniques like operator overloading. This is just the beginning of what you can achieve by diving deeper into Python’s object-oriented features.

If you’re serious about mastering Python, check out our pre-recorded python comprehensive video course.

Conclusion

In this post, you’ve learned a fundamental Python skill: using the __str__ magic method to create human-readable string representations for your objects. You now know that magic methods are special dunder methods that give your classes built-in Python behaviors. By implementing __str__, you can make your custom objects directly compatible with the print() function, leading to cleaner, more professional code. Keep practicing by adding __str__ to all your custom classes—it’s a habit that will serve you well!

Common Questions About Python Magic Methods

What’s the difference between __str__ and __repr__?
__str__ is meant for a readable, “informal” string representation for end-users. __repr__ is meant for an “official,” unambiguous string representation, often used for debugging, that should ideally look like a valid Python expression to recreate the object. If __str__ isn’t defined, Python will fall back to using __repr__.

Do I always need to use magic methods?
No, they are optional. However, using them makes your classes behave more like Python’s built-in types, which makes your code more intuitive and “Pythonic.” They are a mark of well-designed classes.

Are there other magic methods besides __init__ and __str__?
Absolutely! Python has many of them. For example, __len__ for the len() function, __add__ for the + operator, and __getitem__ for indexing. They allow you to overload operators and built-in functions.