String Representations for Classes

Trey Hunner smiling in a t-shirt against a yellow wall
Trey Hunner
6 min. read Python 3.7—3.11
Share
Copied to clipboard.
Tags

Here's a Python class:

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

If we make an instance of this class and then look at that instance from the REPL, we'll see the string representation of that Point object:

>>> p = Point(1, 2)
>>> p
<__main__.Point object at 0x7fdf0c5961f0>

The default string representation for class instances is pretty unhelpful. All we see is that we have an object and it has the type of Point.

How can we make this string representation more helpful?

Customizing the string representation

You can customize the string representation of class instances by writing a __repr__ method for our Point class:

class Point:

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

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

Now when we look at instances of our Point class at the Python REPL, we'll see something that looks like the code we used to create our point:

>>> p = Point(1, 2)
>>> p
Point(1, 2)
>>> p.x = 4
>>> p
Point(4, 2)

If we convert our Point objects to a string or print them, we'll also see our new string representation:

>>> str(p)
'Point(4, 2)'
>>> print(p)
Point(4, 2)

If your class doesn't have a nice string representation and you'd like to customize it, writing a __repr__ method is usually the way to do accomplish that.

The two types of string representations

Python actually has two different string representations for all objects. One string representation is the human-readable one and the other is the programmer-readable one.

The __repr__ method specifies the programmer-readable string representation. If you want just one string representation, this is the one to specify.

The human-readable string representation is specified by the __str__ method. Our Point objects actually already have a __str__ method:

>>> p.__str__()
'Point(4, 2)'
>>> p.__repr__()
'Point(4, 2)'

The default __str__ implementation just calls __repr__:

    def __str__(self):
        return self.__repr__()

The default __repr__ implementation looks something like this:

    def __repr__(self):
        cls = type(self)
        return f"<{cls.__module__}.{cls.__name__} object at {hex(id(self))}>"

So unless you need a second string representation for your object, you can get away with just implementing the __repr__ method.

You can read more on Python's two string representations here.

When __repr__ and __str__ are used

Here's a class that specifies both the __str__ and __repr__ methods:

class Point:

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

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

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

Typing the name of our object at the Python REPL will use the __repr__ method and so will explicitly calling the built-in repr function:

>>> p = Point(1, 2)
>>> p
Point(1, 2)
>>> repr(p)
'Point(1, 2)'

Printing our object will use the __str__ method and so will the built-in str function and string formatting:

>>> print(p)
Point at (1, 2)
>>> str(p)
'Point at (1, 2)'
>>> f"p is {p}"
'p is Point at (1, 2)'

The __repr__ method is used in places where a programmer would be looking at this object (in the REPL and sometimes in things like log files). The __str__ method is used in places where an end-user of our code might be looking at a representation of our object (for example when print it).

When should you write a __str__ method?

In general, you'll only want a __str__ method if you need two different string representations for your object.

If you write a __str__ method but no __repr__ method:

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def __str__(self):
        return f"Point at ({self.x}, {self.y})"

You'll find that the programmer-readable representation of your object wasn't customized:

>>> p = Point(1, 2)
>>> print(p)
Point at (1, 2)
>>> p
<__main__.Point object at 0x7fdf0c5850a0>

Most of the time you'll only need one string representation, so when in doubt just write a __repr__ method.

A notable exception: when you're inheriting from an object which has implemented __repr__ to call __str__ (rather than the other way around, as is the default) you'll probably want to customize just the __str__ method. One of the few places I've seen this in Python is in the Django web framework. Django models customize these two methods so that __repr__ relies on __str__ so you'll almost always want to specify a __str__ method on your Django models but not a __repr__ method.

What objects actually have two string representations?

Most objects in Python has just one string representation.

When you print a list (the human-readable representation) you'll see the same thing as when you reference the list at the REPL (the programmer-readable representation):

>>> x = [1, 2, 3]
>>> print(x)
[1, 2, 3]
>>> x
[1, 2, 3]

Integers and floating point numbers also have just one string representation:

>>> y = 4
>>> str(y)
'4'
>>> repr(y)
'4'

Lists, tuples, sets, dictionaries, integers, floating point numbers, booleans, and None all have just one string representation: a programmer-readable representation.

In the Python standard library you'll find a number of objects that have two string representations though. For example datetime and timedelta objects in the datetime module have two string representations:

>>> from datetime import datetime, timedelta
>>> d = datetime(2020, 1, 1)
>>> d
datetime.datetime(2020, 1, 1, 0, 0)
>>> str(d)
'2020-01-01 00:00:00'
>>> t = timedelta(days=40)
>>> t
datetime.timedelta(days=40)
>>> print(t)
40 days, 0:00:00

And it might not seem like it at first, but strings actually have two string representations. Referencing a string at the REPL and calling str on the string might seem to do the same thing:

>>> s = "hello"
>>> s
'hello'
>>> str(s)
'hello'

But if you print the string you'll see that there's a difference:

>>> print(s)
hello
>>> s
'hello'

The human-readable representation for strings doesn't have quotes but the programmer readable one does:

>>> str(s)
'hello'
>>> repr(s)
"'hello'"

This difference is more obvious with strings that have escape characters in them:

>>> s = "hello\nworld"
>>> s
'hello\nworld'
>>> print(s)
hello
world
>>> str(s)
'hello\nworld'
>>> repr(s)
"'hello\\nworld'"

So how do I make a string representation for my class?

The easy answer is make a __repr__ method because most classes in Python should have just one universal string representation.

While there are two methods for string representations in Python (__str__ and __repr__) the __str__ method calls __repr__ by default, so if you're going to make just one string representation for your class the __repr__ is the method you're looking for.

class Point:

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

    def __repr__(self):
        return f"{type(self).__name__}({self.x!r}, {self.y!r})"

Still not sure you get it? Read more on __repr__ and __str__ here.

A Python Tip Every Week

Need to fill-in gaps in your Python skills? I send weekly emails designed to do just that.