The Difference Between str() and repr() in Python: A Design by Contract Perspective

The Difference Between str() and repr() in Python: A Design by Contract Perspective Featured Image

Recently, I was giving a lecture about Java’s “common” methods (i.e., all of the methods of Object), and I had epiphany about how Java only has toString() while Python has str() and repr(). So, it got me thinking: why did Python go with two different methods?

Table of Contents

Converting Objects to Strings

A common desire in many programming languages is to convert the data we’re storing to text that is readable by a variety of audiences. For example, in Java, there is a method called toString(), which all objects inherit. By default, it prints out an address-like value. When overridden, it can print out anything you want.

One thing that always sort of bothered me about methods like toString() is that there’s only ever a single implementation. In other words, languages never really give the user an opportunity to provide different string values depending on the audience.

I think I fully came to terms with this issue when I started learning (and consequently teaching) design by contract. Under design by contract, we think about software in terms of two key audiences: clients and implementers. At any given time, we’re both of those; clients of the methods we use and implementers of the methods we write.

Therefore, shouldn’t there be two separate toString() methods? One clients can use, and one implementers can use. After all, clients really only care about the things they can see, not the internal details. So, why have a toString() method that just dumps of bunch of logging information? On the flip side, a client facing toString() is almost useless to an implementer because it doesn’t tell them about internal values.

Hopefully, by this point, you get where I’m going with this. Python has two different toString() methods: str() and repr().

Introducing str() and repr()

There are loads of articles on the internet that will describe the two different string methods in Python, so I won’t rehash a lot of what others have said. That said, the general difference between str() and repr() is that str() is meant to be human-readable while repr() is meant to be loaded with information.

As a result, going back to the concept of design by contract, str() is the client-sided method while repr() is the implementer-sided method. This becomes much more clear when you see where each method is typically used. For example, when you print an object in Python, the print() method makes use of str(). Because printing is generally meant to provide information to the user and not for debugging, this makes sense.

Another example that comes to mind is string formatting. By default, when you build an f-string, it calls the str() method. Presumably, f-strings must be targeted at the client. This becomes clear because debugging tools like logging don’t want you to use f-strings at all. Though, that could be totally unrelated.

Python Might Not Have Had Design by Contract in Mind

While design by contract helps us reason about the difference between str() and repr(), I think there’s a more interesting reason for the existence of the two methods. Specifically, you can print out their “help” description to see what I mean:

help(repr)
Help on built-in function repr in module builtins:

repr(obj, /)
    Return the canonical string representation of the object.
    
    For many object types, including most builtins, eval(repr(obj)) == obj.

Okay, you don’t really get a nice message like this for str() since it’s a class. Regardless, I think there’s an interesting secret here: repr() was included with the intent of being able to generate Python code. In other words, according to the docs, repr() should return a string that can be evaluated to create a copy of the original object.

One of the best examples of this is Python dataclasses, which let you create objects with even less boilerplate (i.e., no constructor, no getters, no setters, etc.). By default, a dataclass prints its own constructor out with all of the needed fields filled in:

from dataclasses import dataclass
@dataclass
class Fireball:
    temperature: int
    radius: int
    
attack = Fireball(212, 5)
print(attack)  # prints Fireball(temperature=212, radius=5)

And if you play around, you’ll see that both str() and repr() print the same thing:

repr(attack)  # returns 'Fireball(temperature=212, radius=5)'
str(attack)  # returns 'Fireball(temperature=212, radius=5)'

This is perhaps unsurprising because str() calls repr() if str() is not implemented. Regardless, I found this feature so interesting that I tried to repeat it in my own libraries. For example, in my SnakeMD projectOpens in a new tab., you can actually print an entire markdown document as its Python code, even if you don’t build the document using objects directly. The result is a reproducible document that serves almost like its own programming language (e.g., it reminds me of HTML). Take a look:

import snakemd

doc = snakemd.new_doc()
doc.add_heading("My Cool Document")
doc.add_paragraph("Check out how cool this is!")

str(doc)  # returns '# My Cool Document\n\nCheck out how cool this is!'
repr(doc)  # returns "Document(elements=[Heading(text=[Inline(text='My Cool Document', image=None, link=None, bold=False, italics=False, strikethrough=False, code=False)], level=1), Paragraph(content=[Inline(text='Check out how cool this is!', image=None, link=None, bold=False, italics=False, strikethrough=False, code=False)])])"

Though, at the time of writing, I’m noticing that this feature is still in beta. Maybe I will release it now.

Until Next Time

With all that said, hopefully you have a new perspective on strings in programming languages. While many programming languages have string features, the fact that Python split theirs out into two meaningful methods gives us a lot of power as developers.

As usual, if you liked this, there’s a whole lot more like it throughout the site. At the time of writing, this is the 577th article published on this site. Surely, at least one of the following is interesting to you:

And if you enjoy reading stuff that I didn’t write, check some of these out (#ad):

Finally, you can take your support even further beyond by heading to my list of ways to grow the site. See you next time!

Coding Tangents (43 Articles)—Series Navigation

As a lifelong learner and aspiring teacher, I find that not all subjects carry the same weight. As a result, some topics can fall through the cracks due to time constraints or other commitments. Personally, I find these lost artifacts to be quite fun to discuss. That’s why I’ve decided to launch a whole series to do just that. Welcome to Coding Tangents, a collection of articles that tackle the edge case topics of software development.

In this series, I’ll be tackling topics that I feel many of my own students have been curious about but never really got the chance to explore. In many cases, these are subjects that I think deserve more exposure in the classroom. For instance, did you ever receive a formal explanation of access modifiers? How about package management? Version control?

In some cases, students are forced to learn these subjects on their own. Naturally, this forms a breeding ground for misconceptions which are made popular in online forums like Stack Overflow and Reddit. With this series, I’m hoping to get back to the basics where these subjects can be tackled in their entirety.

Jeremy Grifski

Jeremy grew up in a small town where he enjoyed playing soccer and video games, practicing taekwondo, and trading Pokémon cards. Once out of the nest, he pursued a Bachelors in Computer Engineering with a minor in Game Design. After college, he spent about two years writing software for a major engineering company. Then, he earned a master's in Computer Science and Engineering. Today, he pursues a PhD in Engineering Education in order to ultimately land a teaching gig. In his spare time, Jeremy enjoys spending time with his wife, playing Overwatch and Phantasy Star Online 2, practicing trombone, watching Penguins hockey, and traveling the world.

Recent Posts