Python Arena
Step 4 of 5

__str__ and __repr__

A value can be shown as text in two ways. One is for people: readable text, with no extra marks. The other is for programmers: a technical representation that shows exactly what the value is. For a string, the difference is the quotes. The function repr gives the technical one:

In the Python interpreter
>>> name = "Ada"
>>> print(name)
Ada
>>> print(repr(name))
'Ada'
>>> print(repr(7))
7
>>> print([name, "7", 7])
['Ada', '7', 7]

You have seen the technical text many times without knowing it: a list prints its items with repr. That's why the strings in a printed list have quotes, and why '7' and 7 look different there. The interactive interpreter shows values with repr too.

repr for your own classes

print, str and f-strings use an object's __str__. The function repr, and a list being printed, use its __repr__. A good __repr__ returns text that looks like the Python code that would create an object with the same contents:

class Order:
    def __init__(self, table, dishes):
        self.table = table
        self.dishes = dishes

    def __str__(self):
        return f"table {self.table}: {', '.join(self.dishes)}"

    def __repr__(self):
        return f"Order({self.table}, {self.dishes})"


first = Order(4, ["soup", "pie"])
print(first)
print(f"Next up, {first}")
print(repr(first))
orders = [first, Order(7, ["tea"])]
print(orders)
Output
table 4: soup, pie
Next up, table 4: soup, pie
Order(4, ['soup', 'pie'])
[Order(4, ['soup', 'pie']), Order(7, ['tea'])]

The kitchen reads the __str__ text, and a programmer looking at the list of orders sees exactly what each order holds. The list of dishes could go into __repr__'s f-string as it is, because a list always shows its items with repr: the dishes get their quotes by themselves. A string on its own doesn't. An f-string puts in its bare text, and repr adds the quotes:

In the Python interpreter
>>> dish = "soup"
>>> print(f"Order({dish})")
Order(soup)
>>> print(f"Order({repr(dish)})")
Order('soup')

So when an attribute is a string, write repr(...) around it in __repr__, and the text reads like the code that makes the object.

Without a __repr__, a printed list of orders would show something like <__main__.Order object at 0x7f3a2c1d5e50> for each one: the class and an address in the computer's memory, which helps nobody.

When there's only repr

If a class has __repr__ but no __str__, print uses __repr__. For a small class whose technical text reads well, that one method is enough for both:

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

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


p = Point(2, 5)
print(p)
print([p, Point(0, 0)])
Output
Point(2, 5)
[Point(2, 5), Point(0, 0)]

It doesn't work the other way round: a list never uses __str__ for its items. So when your objects end up in lists, and you want to see them while you test your program, give the class a __repr__.

Your task

Challenge 1 of 3

What does this program print? Type each line it prints.

Challenge 2

Give the class Planet two text forms. __str__ returns a sentence for people, such as Mars has 2 moons. __repr__ returns the code that would create the planet, such as Planet('Mars', 2). For example,

mars = Planet("Mars", 2)
earth = Planet("Earth", 1)
print(mars)
print(repr(mars))
print([mars, earth])

should print:

Mars has 2 moons
Planet('Mars', 2)
[Planet('Mars', 2), Planet('Earth', 1)]

Challenge 3

Write the class Position, a square on a game board, from scratch:

  • The constructor takes x and y.
  • __repr__ returns text such as Position(3, 1). There's no __str__: __repr__ is enough.
  • __eq__ says two positions are equal when both x and y are the same.
  • moved(dx, dy) returns a new position, dx squares further right and dy squares further up.

For example,

start = Position(0, 0)
goal = Position(3, 1)
print(start.moved(2, 1))
print(start.moved(3, 1) == goal)
print([start, goal])

should print:

Position(2, 1)
True
[Position(0, 0), Position(3, 1)]