I just discovered Python’s f-Strings and I love ’em already!

When switching from Java to Python I was so relieved to find out about f-strings. String formatting is so much easier—let me show you why

By Christos Athinaiou

First things first, I know f-strings aren’t exactly hot off the press, but I’m originally a Java developer — I had to switch to Python for a new project.

One of the first problems I had to tackle was how to format strings that contain variables. For those of you who aren’t familiar with Java, you do it like this:

System.out.printf("<strong>How long is</strong> %s? <strong>Well, it's </strong>%d %n <strong>characters long of course!</strong>", "<strong>a piece of string</strong>", "<strong>a piece of string</strong>".length());<em>/* OUTPUT How long is a piece of string? Well, it’s 17 characters long of course! */</em>Code language: HTML, XML (xml)

I wanted to see how you do it Python, so I simply Googled “python string formatting”.

When I entered my search, most of the top search results described the older ways to handle strings in Python (this might have changed since I write this article).

In any case, Google’s “featured snippet” on string formatting (taken from learnpython.org) informed me that “Python uses C-style string formatting…”

“Great” I thought, “I know how that works” (The syntax Java is not dramatically different). So my previous Java statement would look like this in Python.

print("<strong>How long is </strong>%s? <strong>Well, it's </strong>%s<strong> characters long of course!</strong>" % ("<strong>a piece of string</strong>", len("<strong>a piece of string</strong>")))# OUTPUT: How long is a piece of string? Well, it's 17 characters long of course!Code language: HTML, XML (xml)

It’s a bit simpler than Java, but still has some of the associated complexities. To explain, let me show you a longer example. It’s a story generator:

import random
# Create lists of elements that we want to use for a story
names = ["Arjun","Yuuma","Darcy","Mia","Chiaki","Izzi","Azra","Lina"]
creatures = ["unicorn","raven","sparrow","scorpion","coyote","eagle","owl","lizard","zebra","duck","kitten”]
moods = ["vexed","indignant","impassioned","wistful","astute","courteous"]
# Select a random item from each list and put it in a variable
protagonist = names[random.randint(-1, len(names)-1)]
creature = creatures[random.randint(-1, len(creatures)-1)]
protagonist_mood = moods[random.randint(-1, len(moods)-1)]
creature_mood = moods[random.randint(-1, len(moods)-1)]
# Use our variables to create a short story with a sentence template
print("%s traveled with her pet %s. %s was never %s, for the %s was always too %s."
     % (protagonist, creature, protagonist, protagonist_mood, creature, creature_mood))
# Azra traveled with her pet kitten. Azra was never astute, for the kitten was always too vexed.
# Chiaki traveled with her pet raven. Chiaki was never courteous, for the raven was always too astute.
# Lina traveled with her pet raven. Lina was never vexed, for the raven was always tooCode language: PHP (php)

This example tries to emulate a context-free grammar using only Python’s native string formatting. It was inspired by the Javascript library tracerywhich uses context-free grammars to build sentences out of smaller strings. You can use Tracery to create generative stories and interactive text-based games.

A context-free grammar gives us a nice way to examine the complexity of handling a string with lots of variables.

To see why, let’s zoom in on this line:

print("%s <strong>traveled with her pet</strong> %s. %s <strong>was never</strong> %s, <strong>for the</strong> %s <strong>was always too</strong> %s." % (protagonist, creature, protagonist, protagonist_mood, creature, creature_mood))Code language: HTML, XML (xml)

It’s quite difficult to read the statement and anticipate what the sentence will look like. You have to jump back and forth between the sentence template and the variable list to understand what kind of output you’ll get.

Notice that I’ve also had to repeat the protagonist and creature variables because the “%s” replacements are resolved according to the order in which the variables listed. The operator %s is used six times so the structure expects six variables.

There are workarounds to solve this problem, but it’s easier to use Python’s newer advanced string formatting. This method uses curly braces instead of the % operator and you’re no longer restricted by the order of the variables.

With this newer formatting style, our previous example would look like this:

print("{0} <strong>traveled with her pet</strong> {1}. {0} <strong>was never</strong> {2}, <strong>for the</strong> {1} <strong>was always too</strong> {3}."<br>     % (protagonist, creature, protagonist_mood, creature_mood))Code language: HTML, XML (xml)

You can put an index number between the curly braces to indicate the variable that you want to retrieve. The variable animal is at second in the list (technically position 1), so we reference it with {1}. Conveniently, it’s also the second replacement required in the sentence template.

But it’ referenced again, as the fifth replacement near the end of the sentence. So we have to mentally separate the order of replacements and from the index numbers that they reference (replacement number 5 uses the variable at position 1).

We’ve reduced the number of variables but in some ways, the output is even harder to anticipate (at least at a glance).

So this is the moment I’ve been building up to, and hopefully, you understand why I’ve used context-free grammars as an example.

Drum roll please……introducing……the f-string! (OK, it’s been out for just over three years, since Python 3.6 but humor me).

This type of example is so much easier to understand when we do it f-string style!

print(f"{protagonist}<strong> traveled with her pet </strong>{creature}. {protagonist}<strong> was never </strong>{protagonist_mood}<strong>, for the </strong>{creature}<strong> was always too </strong>{creature_mood}.")Code language: HTML, XML (xml)

Look at that! No more switching back and forth between the sentence template and the list of variables. You simply reference them directly in the context where the replacement occurs.

Of course, f-strings are more powerful than this simple example, the website “Real Python” has a helpful article which explains the full power of the f-string. I just wanted to introduce f-strings to people like me who are switching from another language to Python and want a more efficient way of working with strings. You can naturally find more thorough details in the Python documentation.

And here’s one more bonus tip: Suppose that you want to convert existing projects which have older style string formatting and update the formatting to use f-strings everywhere. You do this automatically with the flynt module which is available for Python 3.6 or later.

Hopefully, these tips make your life just a tiny bit easier.