Skip to content
Theory · Classes · lesson 3 of 11

__str__, __repr__ and __eq__

In the previous lesson, we filled in cards from a blueprint. Now imagine a card has to be shown to someone or compared with another one. A person would first read what is written on it. Python cannot do that: it can neither show a new class's instance readably nor compare it by content.

Let's look at a point with coordinates:

Python
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y


p = Point(1, 2)
print(p == Point(1, 2))  # False
print(p)

There are two surprises here:

  • p == Point(1, 2) gives False, although the coordinates are the same;
  • the last line prints something like <__main__.Point object at 0x7f3a2c1b2d50>: the class name and the object's address in memory, which changes from run to run.

By default, == checks whether it is the same object, not whether the coordinates match. A class can replace both behaviors with its own.

Special methods

Methods whose names begin and end with two underscores are special methods. They are usually not called by name. Python calls them itself in certain situations, as it calls __init__ when an instance is created.

A class has two such methods for showing an object. They answer different questions, so let's take each one.

__str__ — for people

The __str__ method returns a string for the program's reader. print, str() and f-strings show it. It is like a caption on the card in large letters: "point (1, 2)".

__repr__ — for the programmer

The __repr__ method returns a string for the programmer. The repr() function shows it, and so do lists, tuples and dicts for their elements. The text of __repr__ usually looks like the call that creates the same object: Point(1, 2).

Let's put both methods into one class:

Python
class Point:
    def __init__(self, x, y):
        self.x, self.y = x, y

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

    def __repr__(self):
        return f"Point({self.x}, {self.y})"


p = Point(1, 2)
print(p)  # (1, 2)
print([p, Point(3, 4)])  # [Point(1, 2), Point(3, 4)]
print(f"p = {p}", repr(p))  # p = (1, 2) Point(1, 2)

Let's go through the output line by line:

  1. print(p) calls __str__ and prints (1, 2).
  2. print([p, Point(3, 4)]) prints a list, and a list shows each element through __repr__.
  3. An f-string takes __str__, and repr(p) takes __repr__.

Here unpacking writes the __init__ line more briefly: self.x, self.y = x, y. The quotes in ['a'] are part of __repr__ too: that is how a string shows itself.

The order of methods in a class does not matter. If a class has no __str__, print uses __repr__. So if you write only one of the two, write __repr__.

Comparing with __eq__

Picture two copies of the same book. Their text is the same, but there are two books on the shelf, not one. In Python, these are two different questions:

  • a is b asks whether it is one object;
  • a == b asks whether the objects are equal by content.

Without a method of its own, a class's == answers the first question, like is: an object equals only itself. The __eq__(self, other) method defines comparison by content. Python calls it for a == b, passing a as self and b as other:

Python
class Point:
    def __init__(self, x, y):
        self.x, self.y = x, y

    def __eq__(self, other):
        return self.x == other.x and self.y == other.y


p = Point(1, 2)
print(p == Point(1, 2), p is Point(1, 2))  # True False
print(p != Point(3, 4))  # True

__eq__ returns True or False. Python derives the != operator from __eq__ itself. The method does not affect is: two calls of Point(1, 2) create different objects.

other receives any object to the right of ==. Here p == 5 stops with an AttributeError, because a number has no attribute x.

Interviewers like to ask: "How does == differ from is?" is compares objects by identity, that is, it checks whether they are one object. == calls __eq__ and compares by content, if the class defines it.

Common mistake

__str__ returns a number instead of a string:

Python
class Point:
    def __init__(self, x):
        self.x = x

    def __str__(self):
        return self.x  # ✗


print(Point(1))
TEXT
TypeError: __str__ returned non-string (type int)

print requires __str__ and __repr__ to return a str. Build the string with an f-string: return f"{self.x}".

Key points

  • __str__ gives the string for print, str() and f-strings, and __repr__ the one for the programmer and for list elements.
  • Without __str__, print uses __repr__.
  • Without __eq__, == compares objects like is; __eq__ defines comparison by content.
  • __str__ and __repr__ must return a str.

Subscribe to take the course

The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.

Sign in another way