Python Magic Methods

In the last lesson you learned Abstraction, where you hide the messy details and show only what people need. Now we go one step further. We will make your own objects behave like Python’s built-in ones. So things like print(), len(), ==, and + will just work on them. The tools that make this happen are called magic methods.

🤔 Why Magic Methods?

Say you build a Book class. You create a book and try to print it.

class Book:
def __init__(self, title, pages):
self.title = title
self.pages = pages
book = Book("Atomic Habits", 320)
print(book)

Here is what Python shows you.

Output

<__main__.Book object at 0x000001F2A3B5C7D0>

That is not helpful, right? You wanted the title, but you got a memory address. The same pain shows up with len(book) or book1 == book2. Python does not know what those mean for your object. So it either gives you junk or an error.

Magic methods fix this. They are special methods Python calls for you when you use built-in operations. You write them once. Then print, len, ==, and more start working the way you expect.

🧩 What Magic Methods Are

A magic method is a method with two underscores before and after its name. People call them dunder methods, which is short for “double underscore”. You have already met one of them: __init__.

The thing to understand is that you almost never call these methods by their full name. Python calls them for you when you use the matching syntax.

  • You write print(book) and Python calls book.__str__().
  • You write len(book) and Python calls book.__len__().
  • You write book1 == book2 and Python calls book1.__eq__(book2).
  • You write book1 + book2 and Python calls book1.__add__(book2).

So the regular syntax stays clean and short. The magic method runs in the background. It decides what that syntax actually does for your object.

Here are the ones we will cover.

Magic method You write It controls
__init__ Book(...) How a new object is set up
__str__ print(book) The friendly text version of the object
__len__ len(book) What its length means
__eq__ book1 == book2 When two objects count as equal
__add__ book1 + book2 What adding two objects does

🛠️ init (a quick recap)

You met __init__ already, so this is just a reminder. It is the magic method Python calls when you create an object. Its job is to set up the starting values.

This code sets up a book with a title and a page count.

class Book:
def __init__(self, title, pages):
self.title = title
self.pages = pages
book = Book("Atomic Habits", 320)
print(book.title)
print(book.pages)

Output

Atomic Habits
320

When you write Book("Atomic Habits", 320), Python calls __init__ and passes those values in. The self part is the new object being built. So self.title and self.pages save those values onto the object. Now the rest of the magic methods can use them.

🖨️ str (a nice print output)

Remember the ugly memory address from earlier? __str__ is the fix. It tells Python what text to show when someone prints your object.

We add a __str__ method that returns a friendly sentence.

class Book:
def __init__(self, title, pages):
self.title = title
self.pages = pages
def __str__(self):
return f"'{self.title}' ({self.pages} pages)"
book = Book("Atomic Habits", 320)
print(book)

Output

'Atomic Habits' (320 pages)

Look at what changed. We did not call __str__ ourselves. We just wrote print(book) like normal. Python saw the print, looked for a __str__ method on the object, found ours, and used the text it returned. Just remember __str__ has to return a string. It should not print it.

Tip

Think of __str__ as the label you stick on the object. Whenever something needs to show your object as text, that label is what shows up.

📏 len (so len() works)

Now say you want len(book) to give back the number of pages. Out of the box, Python has no idea what the length of a book is. So it raises an error. __len__ tells it.

We add __len__ and return the page count.

class Book:
def __init__(self, title, pages):
self.title = title
self.pages = pages
def __len__(self):
return self.pages
book = Book("Atomic Habits", 320)
print(len(book))

Output

320

When you write len(book), Python calls book.__len__() for you and shows whatever number it returns. Just remember that __len__ has to give back a whole number that is zero or more. If you try to return text or a decimal, Python complains.

⚖️ eq (so == compares fairly)

This one solves a sneaky problem. Make two books with the same title and pages, then check if they are equal.

class Book:
def __init__(self, title, pages):
self.title = title
self.pages = pages
book1 = Book("Atomic Habits", 320)
book2 = Book("Atomic Habits", 320)
print(book1 == book2)

Output

False

Wait, the same title and the same pages, but Python says False? That is because by default == checks if they are the exact same object in memory, not whether their values match. These are two separate objects, so Python says no.

We fix this with __eq__. It lets you decide what “equal” means for your books.

class Book:
def __init__(self, title, pages):
self.title = title
self.pages = pages
def __eq__(self, other):
return self.title == other.title and self.pages == other.pages
book1 = Book("Atomic Habits", 320)
book2 = Book("Atomic Habits", 320)
print(book1 == book2)

Output

True

Now it says True. When you write book1 == book2, Python calls book1.__eq__(book2). Inside, self is book1 and other is book2. We compare their titles and their pages. If both match, we return True. So now two books with the same details count as equal, which is what a reader would expect.

add (so + works)

Last one. Say you want to add two books and get the total number of pages, maybe to plan your reading. __add__ controls what + does.

We add __add__ and return the two page counts added together.

class Book:
def __init__(self, title, pages):
self.title = title
self.pages = pages
def __add__(self, other):
return self.pages + other.pages
book1 = Book("Atomic Habits", 320)
book2 = Book("Deep Work", 304)
print(book1 + book2)

Output

624

When you write book1 + book2, Python calls book1.__add__(book2). Here self is the book on the left and other is the book on the right. We add their pages and return the total. You decide what + means, so you could just as easily join titles or do anything else that makes sense.

⚠️ Common Mistakes

A few things trip people up with magic methods.

The first one is forgetting to return instead of print inside __str__. Python expects a string back, not a print call.

# ❌ Avoid: this returns None, so print shows a blank line
def __str__(self):
print(f"'{self.title}'")
# ✅ Good: return the string and let Python handle the printing
def __str__(self):
return f"'{self.title}'"

Another one is forgetting the other parameter in methods that compare or combine two objects.

# ❌ Avoid: __eq__ needs the other object to compare against
def __eq__(self):
return self.title
# ✅ Good: accept other so you can compare both sides
def __eq__(self, other):
return self.title == other.title

A couple more to keep in mind.

  • Misspelling the name, like __str_ with one underscore. Python will not recognize it, so your version never runs.
  • Returning the wrong type. __len__ must return a whole number, and __str__ must return a string.

✅ Best Practices

Some simple habits that keep magic methods clean.

  • Add __str__ to almost every class. A clear print output makes everything easier when you are checking your code.
  • Keep magic methods short. They should do one obvious thing, not run heavy logic.
  • Make the meaning match the syntax. + should feel like adding, == should feel like comparing. Do not surprise the reader.
  • Use real attributes inside them, the same ones you set in __init__.

🧩 What You’ve Learned

A quick recap of what you can do now.

  • ✅ Magic methods are special methods Python calls for you behind regular syntax.
  • ✅ They are also called dunder methods because of the double underscores.
  • __init__ sets up a new object’s starting values.
  • __str__ gives a friendly text version for print().
  • __len__ makes len() work on your object.
  • __eq__ lets you decide when two objects count as equal.
  • __add__ controls what + does with your objects.

Check Your Knowledge

Test what you learned. Pick an answer for each question, then click Check.

  1. 1

    Why are magic methods also called dunder methods?

    Why: Dunder is short for 'double underscore', which is the two underscores on each side of the name.

  2. 2

    What does Python call when you write print(book)?

    Why: print() looks for a __str__ method on the object and uses the string it returns.

  3. 3

    Two Book objects have the same title and pages but book1 == book2 prints False. What fixes it?

    Why: By default == checks identity in memory; __eq__ lets you compare values instead.

  4. 4

    Inside __add__(self, other), what do self and other refer to?

    Why: For book1 + book2, self is book1 (left side) and other is book2 (right side).

🚀 What’s Next?

You can now make your own objects feel like built-in Python types, which is a big step. Next we group related code into reusable files so your projects stay tidy as they grow.

Python Modules

Share & Connect