Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: Python 3's f-Strings: An Improved String Formatting Syntax
As of Python 3.6, f-strings are a great new way to format strings. Not only are they more readable, more concise, and less prone to error than other ways of formatting, but they are also faster!
By the end of this article, you will learn how and why to start using f-strings today.
But first, here’s what life was like before f-strings, back when you had to walk to school uphill both ways in the snow.
Take the Quiz: Test your knowledge with our interactive “Python f-Strings” quiz. Upon completion you will receive a score so you can track your learning progress over time:
“Old-school” String Formatting in Python
Before Python 3.6, you had two main ways of embedding Python expressions inside string literals for formatting: %-formatting and str.format(). You’re about to see how to use them and what their limitations are.
Option #1: %-formatting
This is the OG of Python formatting and has been in the language since the very beginning. You can read more in the Python docs. Keep in mind that %-formatting is not recommended by the docs, which contain the following note:
“The formatting operations described here exhibit a variety of quirks that lead to a number of common errors (such as failing to display tuples and dictionaries correctly).
Using the newer formatted string literals or the
str.format()interface helps avoid these errors. These alternatives also provide more powerful, flexible and extensible approaches to formatting text.” (Source)
How to Use %-formatting
String objects have a built-in operation using the % operator, which you can use to format strings. Here’s what that looks like in practice:
>>> name = "Eric"
>>> "Hello, %s." % name
'Hello, Eric.'
In order to insert more than one variable, you must use a tuple of those variables. Here’s how you would do that:
>>> name = "Eric"
>>> age = 74
>>> "Hello, %s. You are %s." % (name, age)
'Hello Eric. You are 74.'
Why %-formatting Isn’t Great
The code examples that you just saw above are readable enough. However, once you start using several parameters and longer strings, your code will quickly become much less easily readable. Things are starting to look a little messy already:
>>> first_name = "Eric"
>>> last_name = "Idle"
>>> age = 74
>>> profession = "comedian"
>>> affiliation = "Monty Python"
>>> "Hello, %s %s. You are %s. You are a %s. You were a member of %s." % (first_name, last_name, age, profession, affiliation)
'Hello, Eric Idle. You are 74. You are a comedian. You were a member of Monty Python.'
Unfortunately, this kind of formatting isn’t great because it is verbose and leads to errors, like not displaying tuples or dictionaries correctly. Fortunately, there are brighter days ahead.
Option #2: str.format()
This newer way of getting the job done was introduced in Python 2.6. You can check out A Guide to the Newer Python String Format Techniques for more info.
How To Use str.format()
str.format() is an improvement on %-formatting. It uses normal function call syntax and is extensible through the __format__() method on the object being converted to a string.
With str.format(), the replacement fields are marked by curly braces:
>>> "Hello, {}. You are {}.".format(name, age)
'Hello, Eric. You are 74.'
You can reference variables in any order by referencing their index:
>>> "Hello, {1}. You are {0}.".format(age, name)
'Hello, Eric. You are 74.'
But if you insert the variable names, you get the added perk of being able to pass objects and then reference parameters and methods in between the braces:
>>> person = {'name': 'Eric', 'age': 74}
>>> "Hello, {name}. You are {age}.".format(name=person['name'], age=person['age'])
'Hello, Eric. You are 74.'
You can also use ** to do this neat trick with dictionaries:
>>> person = {'name': 'Eric', 'age': 74}
>>> "Hello, {name}. You are {age}.".format(**person)
'Hello, Eric. You are 74.'
str.format() is definitely an upgrade when compared with %-formatting, but it’s not all roses and sunshine.
Why str.format() Isn’t Great
Code using str.format() is much more easily readable than code using %-formatting, but str.format() can still be quite verbose when you are dealing with multiple parameters and longer strings. Take a look at this:
>>> first_name = "Eric"
>>> last_name = "Idle"
>>> age = 74
>>> profession = "comedian"
>>> affiliation = "Monty Python"
>>> print(("Hello, {first_name} {last_name}. You are {age}. " +
>>> "You are a {profession}. You were a member of {affiliation}.") \
>>> .format(first_name=first_name, last_name=last_name, age=age, \
>>> profession=profession, affiliation=affiliation))
'Hello, Eric Idle. You are 74. You are a comedian. You were a member of Monty Python.'
If you had the variables you wanted to pass to .format() in a dictionary, then you could just unpack it with .format(**some_dict) and reference the values by key in the string, but there has got to be a better way to do this.
f-Strings: A New and Improved Way to Format Strings in Python
The good news is that f-strings are here to save the day. They slice! They dice! They make julienne fries! Okay, they do none of those things, but they do make formatting easier. They joined the party in Python 3.6. You can read all about it in PEP 498, which was written by Eric V. Smith in August of 2015.
Also called “formatted string literals,” f-strings are string literals that have an f at the beginning and curly braces containing expressions that will be replaced with their values. The expressions are evaluated at runtime and then formatted using the __format__ protocol. As always, the Python docs are your friend when you want to learn more.
Here are some of the ways f-strings can make your life easier.
Simple Syntax
The syntax is similar to the one you used with str.format() but less verbose. Look at how easily readable this is:
>>> name = "Eric"
>>> age = 74
>>> f"Hello, {name}. You are {age}."
'Hello, Eric. You are 74.'
It would also be valid to use a capital letter F:
>>> F"Hello, {name}. You are {age}."
'Hello, Eric. You are 74.'
Do you love f-strings yet? I hope that, by the end of this article, you’ll answer >>> F"Yes!".
Arbitrary Expressions
Because f-strings are evaluated at runtime, you can put any and all valid Python expressions in them. This allows you to do some nifty things.
You could do something pretty straightforward, like this:
>>> f"{2 * 37}"
'74'
But you could also call functions. Here’s an example:
>>> def to_lowercase(input):
... return input.lower()
>>> name = "Eric Idle"
>>> f"{to_lowercase(name)} is funny."
'eric idle is funny.'
You also have the option of calling a method directly:
>>> f"{name.lower()} is funny."
'eric idle is funny.'
You could even use objects created from classes with f-strings. Imagine you had the following class:
class Comedian:
def __init__(self, first_name, last_name, age):
self.first_name = first_name
self.last_name = last_name
self.age = age
def __str__(self):
return f"{self.first_name} {self.last_name} is {self.age}."
def __repr__(self):
return f"{self.first_name} {self.last_name} is {self.age}. Surprise!"
You’d be able to do this:
>>> new_comedian = Comedian("Eric", "Idle", "74")
>>> f"{new_comedian}"
'Eric Idle is 74.'
The __str__() and __repr__() methods deal with how objects are presented as strings, so you’ll need to make sure you include at least one of those methods in your class definition. If you have to pick one, go with __repr__() because it can be used in place of __str__().
The string returned by __str__() is the informal string representation of an object and should be readable. The string returned by __repr__() is the official representation and should be unambiguous. Calling str() and repr() is preferable to using __str__() and __repr__() directly.
By default, f-strings will use __str__(), but you can make sure they use __repr__() if you include the conversion flag !r:
>>> f"{new_comedian}"
'Eric Idle is 74.'
>>> f"{new_comedian!r}"
'Eric Idle is 74. Surprise!'
If you’d like to read some of the conversation that resulted in f-strings supporting full Python expressions, you can do so here.