0% found this document useful (0 votes)
11 views24 pages

Lesson 10 - Python User Input, Output and Formatting

This document covers user input and string formatting in Python, focusing on the input() function and various string interpolation methods including f-strings, str.format(), and the modulo operator. It highlights the advantages of f-strings introduced in Python 3.6 for their readability and performance, while also explaining traditional methods. The tutorial includes examples of how to use these methods for dynamic string creation and formatting, emphasizing the flexibility and capabilities of f-strings.

Uploaded by

clintonomondi890
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
11 views24 pages

Lesson 10 - Python User Input, Output and Formatting

This document covers user input and string formatting in Python, focusing on the input() function and various string interpolation methods including f-strings, str.format(), and the modulo operator. It highlights the advantages of f-strings introduced in Python 3.6 for their readability and performance, while also explaining traditional methods. The tutorial includes examples of how to use these methods for dynamic string creation and formatting, emphasizing the flexibility and capabilities of f-strings.

Uploaded by

clintonomondi890
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

Lesson 10 - Python User Input, Output, Interpolating and

Formatting

Table of Contents

 Inputting in python using input() function


 Interpolating and Formatting Strings Before Python 3.6
o The Modulo Operator, %
o The str.format() Method
o Doing String Interpolation With F-Strings in Python
o Interpolating Values and Objects in F-Strings
o Embedding Expressions in F-Strings
o Formatting Strings With Python’s F-String

String Interpolation: This refers to the process of embedding


variables or expressions directly within a string literal, allowing
their values to be substituted into placeholders within the string
at runtime. This creates dynamic strings without needing explicit
string concatenation.

 f-strings (Formatted String Literals): Introduced in Python


3.6, f-strings are the most modern and recommended way to
perform string interpolation in Python due to their readability
and performance. They allow you to embed expressions inside
curly braces {} within a string prefixed with f or F.
 str.format() method: A more traditional method that uses
placeholders (e.g., {}) within a string and then calls
the .format() method with the values to be inserted.
 Old-style % formatting: Similar to C-style printf, this method
uses format specifiers (e.g., %s for string, %d for integer) to
define placeholders. This method is generally less preferred in
modern Python code.

Python inputting

Python allows for user input. That means we are able to ask the
user for input.

The method is a bit different in Python 3.6 than Python 2.7.

1
Python 3.6 uses the input() method and Python 2.7 uses
the raw_input() method.

The following example asks for the username, and when you
entered the username, it gets printed on the screen:

Python 3.6
username = input("Enter username:")
print("Username is: " + username)

Python 2.7
username = raw_input("Enter username:")
print("Username is: " + username)

Python stops executing when it comes to the input() function, and


continues when the user has given some input.
Python outputting

Python f-strings provide a quick way


to interpolate and format strings. They’re readable, concise,
and less prone to error than traditional string interpolation and
formatting tools, such as the .format() method and the modulo
operator (%). An f-string is also a bit faster than those tools!

In this tutorial, you’ll learn how to:

 Interpolate values, objects, and expressions into your


strings using f-strings
 Format f-strings using Python’s string formatting mini-
language
 Leverage some cool features of f-strings in Python 3.12
and beyond
 Decide when to use traditional interpolation tools instead
of f-strings

To get the most out of this tutorial, you should be familiar with
Python’s string data type. It’s also be beneficial to have
experience with other string interpolation tools like the modulo
operator (%) and the .format() method.

2
Interpolating and Formatting Strings

Before Python 3.6, you had two main tools for interpolating
values, variables, and expressions inside string literals:

1. The string interpolation operator (%), or modulo operator


2. The str.format() method
3. f-string

The Modulo Operator, %

The modulo operator (%) was the first tool for string interpolation
and formatting in Python and has been in the language since the
beginning. Here’s what using this operator looks like in practice:

Python
>>> name = "Jane"

>>> "Hello, %s!" % name


'Hello, Jane!'
In this quick example, you use the % operator to interpolate the
value of your name variable into a string literal. The interpolation
operator takes two operands:

 A string literal containing one or more conversion specifiers


 The object or objects that you’re interpolating into the string
literal

The conversion specifiers work as replacement fields. In the


above example, you use the %s combination of characters as a
conversion specifier. The % symbol marks the start of the
specifier, while the s letter is the conversion type and tells the
operator that you want to convert the input object into a string.

If you want to insert more than one object into your target string,
then you can use a tuple. Note that the number of objects in the
tuple must match the number of format specifiers in the string:

Python
>>> name = "Jane"

3
>>> age = 25

>>> "Hello, %s! You're %s years old." % (name, age)


'Hello, Jane! You're 25 years old.'
In this example, you use a tuple of values as the right-hand
operand to %. Note that you’ve used a string and an integer.
Because you use the %s specifier, Python converts both objects to
strings.

You can also use dictionaries as the right-hand operand in your


interpolation expressions. To do this, you need to create
conversion specifiers that enclose key names in parentheses:

Python
>>> "Hello, %(name)s! You're %(age)s years old." % {"name":
"Jane", "age": 25}
"Hello, Jane! You're 25 years old."
This syntax provides a readable approach to string interpolation
with the % operator. You can use descriptive key names instead
of relying on the positional order of values.

When you use the % operator for string interpolation, you can use
conversion specifiers. They provide some string formatting
capabilities that take advantage of conversion
types, conversion flags, and some characters like the period (.)
and the asterisk (*). Consider the following example:

Python
>>> "Balance: $%.2f" % 5425.9292
'Balance: $5425.93'

>>> print("Name: %s\nAge: %5s" % ("John", 35))


Name: John
Age: 35
In the first example, you use the %.2f conversion specifier to
represent currency values. The f letter tells the operator to
convert to a floating-point number. The .2 part defines the
precision to use when converting the input. In the second

4
example, you use %5s to align the age value five positions to the
right.

Note: Formatting with the modulo operator is inspired


by printf() formatting used in C and many other programming
languages.
Even though the % operator provides a quick way to interpolate
and format strings, it has a few issues that lead to common
errors. For example, it’s difficult to interpolate tuples in your
strings:

Python
>>> "The personal info is: %s" % ("John", 35)
Traceback (most recent call last):
...
TypeError: not all arguments converted during string formatting
In this example, the operator fails to display the tuple of data
because it interprets the tuple as two separate values. You can fix
this issue by wrapping the data in a single-item tuple:

Python
>>> "The personal info is: %s" % (("John", 35),)
"The personal info is: ('John', 35)"
This syntax fixes the issue, and now your string successfully
shows the tuple of data. However, the syntax is hard to read,
understand, and remember, isn’t it?

Another issue with the % operator is its limited formatting


capabilities and the lack of support for Python’s string formatting
mini-language, which provides a powerful tool to format your
strings.

The str.format() Method

The str.format() method is an improvement compared to


the % operator because it fixes a couple of issues and supports
the string formatting mini-language. With .format(), curly braces
delimit the replacement fields:

Python

5
>>> name = "Jane"
>>> age = 25

>>> "Hello, {}! You're {} years old.".format(name, age)


"Hello, Jane! You're 25 years old."
For the .format() method to work, you must provide replacement
fields using curly brackets. If you use empty brackets, then the
method interpolates its arguments into the target string based on
position.

You can manually specify the interpolation order by referencing


the position of each argument to .format() using zero-based
indices. For example, the code below switches the arguments
to .format() in the target string:

Python
>>> "Hello, {1}! You're {0} years old.".format(age, name)
"Hello, Jane! You're 25 years old."
In this example, you use numeric indices to manually define the
order in which you want to interpolate the values that you pass as
arguments to .format().

You can also use keyword arguments in the call to the method
and enclose the argument names in your replacement fields:

Python
>>> "Hello, {name}! You're {age} years
old.".format(name="Jane", age=25)
"Hello, Jane! You're 25 years old."
This example showcases how .format() interpolates keyword
arguments by their names into the target string. This construct
considerably improves your code’s readability compared to the
previous example and to the examples using the % operator.

Finally, the .format() method allows you to use dictionaries to


provide the values that you want to interpolate into your strings:

Python
>>> person = {"name": "Jane", "age": 25}

6
>>> "Hello, {name}! You're {age} years old.".format(**person)
"Hello, Jane! You're 25 years old."
In this example, you use a dictionary containing the data to
interpolate. Then, you use the dictionary unpacking operator
(**) to provide the arguments to .format().

The .format() method supports format specifiers. These are


strings that you insert into replacement fields to format the
values that you want to interpolate. Consider the following
examples:

Python
>>> "Balance: ${:.2f}".format(5425.9292)
'Balance: $5425.93'

>>> "{:=^30}".format("Centered string")


'=======Centered string========'
In the first example, you use the :.2f format specifier. This
specifier tells .format() to format the input value as a floating-
point number with a precision of two. This way, you can represent
currency values.

In the second example, you use the :=^30 format specifier. In


this case, you’re telling .format() to format the input value using
the = symbol as a filler character. The ^ symbol centers the input
value by inserting = symbols on both sides to reach thirty
characters.

Format specifiers provide a remarkable improvement over the


limited formatting capabilities of the % operator. These specifiers
have a straightforward syntax that makes up the string formatting
mini-language. Thankfully, f-strings also support the string
formatting mini-language, which is another cool feature of theirs.
So, you won’t have to use .format() if you don’t need to.

In the upcoming sections, you’ll write a few more examples of


formatting strings using the mini-language with f-strings.

7
Doing String Interpolation With F-Strings in Python

F-strings joined the party in Python 3.6. Also called formatted


string literals, f-strings are string literals that have an f before
the opening quotation mark. They can include Python expressions
enclosed in curly braces. Python will replace those expressions
with their resulting values. So, this behavior turns f-strings into a
string interpolation tool.

In the following sections, you’ll learn about f-strings and use them
to interpolate values, objects, and expressions in your string
literals.

Interpolating Values and Objects in F-Strings

F-strings make the string interpolation process intuitive, quick,


and concise. The syntax is similar to what you used with .format(),
but it’s less verbose. You only need to start your string literal with
a lowercase or uppercase f and then embed your values, objects,
or expressions in curly brackets at specific places:

Python
>>> name = "Jane"
>>> age = 25

>>> f"Hello, {name}! You're {age} years old."


'Hello, Jane! You're 25 years old.'
Look how readable and concise your string is now that you’re
using the f-string syntax. You don’t need operators or methods
anymore. You just embed the desired objects or expressions in
your string literal using curly brackets.

It’s important to note that Python evaluates f-strings at runtime.


So, in this example, both name and age are interpolated into the
string literal when Python runs the line of code containing the f-
string. Python can only interpolate these variables because you
defined them before the f-string, which means that they must be
in scope when Python evaluates the f-string.

Embedding Expressions in F-Strings

8
You can embed almost any Python expression in an f-string. This
allows you to do some nifty things. You could do something pretty
straightforward, like the following:

Python
>>> f"{2 * 21}"
'42'
When Python runs this f-string, it multiplies 2 by 21 and
immediately interpolates the resulting value into the final string.

The example above is quite basic. However, f-strings are more


powerful than that. You could also use other Python expressions,
including function and method calls, and even comprehensions or
other more complex expressions:

Python
>>> name = "Jane"
>>> age = 25

>>> f"Hello, {name.upper()}! You're {age} years old."


"Hello, JANE! You're 25 years old."

>>> f"{[2**n for n in range(3, 9)]}"


'[8, 16, 32, 64, 128, 256]'
In the first f-string, you embed a call to the .upper() string method
in the first replacement field. Python runs the method call and
inserts the uppercased name into the resulting string. In the
second example, you create an f-string that embeds a list
comprehension. The comprehension creates a new list of powers
of 2.

Formatting Strings With Python’s F-String

The expressions that you embed in an f-string are evaluated at


runtime. Then, Python formats the result using
the .__format__() special method under the hood. This method
supports the string formatting protocol. This protocol underpins
both the .format() method, which you already saw, and the built-
in format() function:

9
Python
>>> format(5425.9292, ".2f")
'5425.93'
The format() function takes a value and a format specifier as
arguments. Then, it applies the specifier to the value to return a
formatted value. The format specifier must follow the rules of the
string formatting mini-language.

Just like the .format() method, f-strings also support the string
formatting mini-language. So, you can use format specifiers in
your f-strings too:

Python
>>> balance = 5425.9292

>>> f"Balance: ${balance:.2f}"


'Balance: $5425.93'

>>> heading = "Centered string"


>>> f"{heading:=^30}"
'=======Centered string========'
Note that the format specifiers in these examples are the same
ones that you used in the section on .format(). In this case, the
embedded expression comes before the format specifier, which
always starts with a colon. This syntax makes the string literals
readable and concise.

You can create a wide variety of format specifiers. Some common


formats include currencies, dates, and the representation of
numeric values. Consider the following examples of string
formatting:

Python
>>> integer = -1234567
>>> f"Comma as thousand separators: {integer:,}"
'Comma as thousand separators: -1,234,567'

>>> sep = "_"

10
>>> f"User's thousand separators: {integer:{sep}}"
'User's thousand separators: -1_234_567'

>>> floating_point = 1234567.9876


>>> f"Comma as thousand separators and two decimals:
{floating_point:,.2f}"
'Comma as thousand separators and two decimals: 1,234,567.99'

>>> date = (9, 6, 2023)


>>> f"Date: {date[0]:02}-{date[1]:02}-{date[2]}"
'Date: 09-06-2023'

>>> from datetime import datetime


>>> date = datetime(2023, 9, 26)
>>> f"Date: {date:%m/%d/%Y}"
'Date: 09/26/2023'
These examples show how flexible the format specifiers can be.
You can use them to create almost any string format. Note how in
the second example, you’ve used curly brackets to embed
variables or expressions in your format specifiers. This possibility
allows you to create dynamic specifiers, which is pretty cool. In
the last example, you format a datetime which can be formatted
with special date format specifiers.

Other Relevant Features of F-Strings

So far, you’ve learned that f-strings provide a quick and readable


way to interpolate values, objects, and expressions into string
literals. They also support the string formatting mini-language, so
you can create format specifiers to format the objects that you
want to insert into your strings.

In the following sections, you’ll learn about a few additional


features of f-strings that may be relevant and useful in your day-
to-day coding.

Using an Object’s String Representations in F-Strings

11
Python’s f-strings support two flags with special meaning in the
interpolation process. These flags are closely related to how
Python manages the string representation of objects. These flags
are:

Fla
g Description
!s Interpolates the string representation from the .__str__() method
!r Interpolates the string representation from the .__repr__() method
The .__str__() special method generally provides a user-friendly
string representation of an object, while the .__repr__() method
returns a developer-friendly representation. To illustrate how
these methods work under the hood, consider the following class:

Python
# person.py

class Person:
def __init__(self, name, age):
self.name = name
self.age = age

def __str__(self):
return f"I'm {self.name}, and I'm {self.age} years old."

def __repr__(self):
return f"{type(self).__name__}(name='{self.name}',
age={self.age})"
This class has two instance attributes, .name and .age.
The .__str__() method returns a string that consists of an
informative message for users of your class. This message should
be useful for end users rather than developers.

Note: To dive deeper into the __str__() and __repr__() methods,


check out When Should You Use. .__repr__() vs .__str__() in
Python?
In contrast, the .__repr__() method returns a string that’s a
developer-friendly representation of the object. In short, the

12
representation tells the developer how the current instance was
created. Ideally, the developer should be able to copy this string
representation and create an equivalent object.

How does this discussion about string representation affect f-


strings? When you create your f-strings, you can choose which
string representation to use with the !r and !s flags:

Python
>>> from person import Person

>>> jane = Person("Jane Doe", 25)

>>> f"{jane!s}"
"I'm Jane Doe, and I'm 25 years old."

>>> f"{jane!r}"
"Person(name='Jane Doe', age=25)"
In the first f-string, you use the !s tag to interpolate the string
representation that .__str__() returns. In the second f-string, you
use the !r flag to interpolate the developer-friendly string
representation of your current object.

These two flags are pretty relevant for you as a Python developer.
Depending on your code’s intended audience, you can decide
which one to use. In general, it should be the one that provides
more value to your users.

It’s important to note that the % operator also supports


equivalent conversion types, s and r, which work the same as
the !s and !r flags in f-strings.

Self-Documenting Expressions for Debugging

F-strings have another cool feature that can be useful, especially


during your debugging process. The feature helps you self-
document some of your expressions. For example, say that you’re
dealing with a minor bug or issue in your code, and you want to

13
know the value of a variable at a given moment in the code’s
execution.

For this quick check, you can insert a call to print() like the
following:

Python
>>> variable = "Some mysterious value"

>>> print(f"{variable = }")


variable = 'Some mysterious value'
You can use a variable name followed by an equal sign (=) in an f-
string to create a self-documented expression. When Python runs
the f-string, it builds an expression-like string containing the
variable’s name, the equal sign, and the variable’s current value.
This f-string feature is useful for inserting quick debugging checks
in your code.

Note that the whitespaces around the equal sign aren’t required
but they are reflected in the output:

Python
>>> print(f"{variable=}")
variable='Some mysterious value'

>>> print(f"{variable= }")


variable= 'Some mysterious value'

>>> print(f"{variable =}")


variable ='Some mysterious value'
Even though the whitespaces aren’t required, they can improve
your code’s readability and the output’s format.

Comparing Performance: F-String vs Traditional Tools

F-strings are a bit faster than both the modulo operator (%) and
the .format() method. That’s another cool characteristic. In the
script below, you use the timeit module to measure the execution

14
time that it takes to build a string using the modulo operator,
the .format() method, and an f-string:

Python
# performance.py

import timeit

name = "Linda Smith"


age = 40
strings = {
"Modulo operator": "'Name: %s Age: %s' % (name, age)",
".format() method": "'Name: {} Age: {}'.format(name, age)",
"f_string": "f'Name: {name} Age: {age}'",
}

def run_performance_test(strings):
max_length = len(max(strings, key=len))
for tool, string in strings.items():
time = timeit.timeit(
string,
number=1000000,
globals=globals()
) * 1000
print(f"{tool}: {time:>{max_length - len(tool) + 6}.2f} ms")

run_performance_test(strings)
In this script, the run_performance_test() function takes care of
measuring the execution time of the three different string
interpolation tools. The timeit.timeit() function inside
the for loop runs each interpolation tool a million times and
returns the total execution time.

Then, the function prints the result to the screen. Note how your f-
string in the call to print() takes advantage of format specifiers to
conveniently format the code’s output.

15
If you run the script from your command line, then you’ll get an
output similar to the following. Of course, the numbers will be
different for you:

Shell
$ python performance.py
Modulo operator: 90.98 ms
.format() method: 144.69 ms
f_string: 87.08 ms
This output shows that f-strings are a bit faster than
the % operator and the .format() method, which is the slowest
tool because of all the required function calls. So, f-strings are
readable, concise, and also fast.

Upgrading F-Strings: Python 3.12 and Beyond

Now that you’ve learned why f-strings are great, you’re probably
eager to get out there and start using them in your code.
However, you need to know that f-strings up to Python 3.11 have
a few limitations regarding the expressions that you can embed in
curly brackets and a few other details.

Fortunately, Python 3.12 lifted those limitations by removing the


old f-string parser and providing a new implementation of f-
strings based on the PEG parser of Python 3.9. In the following
sections, you’ll learn about the limitations and how Python 3.12
fixed them.

Using Quotation Marks

Python supports several different types of quotation marks as


delimiters in string literals. You can use single (') and double
quotes ("). You can also use triple single (''') and triple double
quotes ("""). All these string delimiters work for f-strings as well.
This feature allows you to insert quotation marks in f-strings. It
also lets you introduce string literals in the embedded expressions
and even create nested f-strings.

16
A typical use case of using different quotation marks in an f-string
is when you need to use an apostrophe or access a dictionary key
in an embedded expression:

Python
>>> person = {"name": "Jane", "age": 25}

>>> f"Hello, {person['name']}! You're {person['age']} years


old."
"Hello, Jane! You're 25 years old."
In this example, you have a dictionary with a person’s data. To
define the f-string, you use double quotes. To access the
dictionary key, you use single quotes. In the "You're" contraction,
you use a single quote as an apostrophe.

So, where’s the quote-related limitation of f-strings up to Python


3.11? The problem is that you can’t reuse quotation marks in an f-
string:

Python
>>> f"Hello, {person["name"]}!"
File "<input>", line 1
f"Hello, {person["name"]}!"
^^^^
SyntaxError: f-string: unmatched '['
In this example, when you try to reuse double quotes to access
the dictionary key, your f-string fails, and Python raises
a SyntaxError exception.

Fortunately, the new f-strings in Python 3.12 solved this issue,


allowing you to reuse quotes:

Python
>>> # Python 3.12

>>> person = {"name": "Jane", "age": 25}


>>> f"Hello, {person["name"]}!"
'Hello, Jane!'

17
In this example, you reuse the double quotes in your embedded
expressions, and the f-string works correctly. The limitation is
gone. However, it may not be clear if reusing quotations in this
example is cleaner than differentiating nested strings with
different quotation marks.

There’s another f-string limitation that’s closely related to


quotation marks. You can only nest as many f-strings as there are
quote delimiters in Python:

Python
>>> f"""{
... f'''{
... f"{f'{42}'}"
... }'''
... }"""
'42'

>>> f"""{
... f'''{
... f"{f'{f"{42}"}'}"
... }'''
... }"""
File "<stdin>", line 1
(f"{f'{f"{42}"}'}")
^
SyntaxError: f-string: f-string: unterminated string
The number of nesting levels in an f-string up to Python 3.11 is
limited by the available string delimiters, which are ", ', """,
and '''. So, you only have four delimiters that you can use to
differentiate your levels of nesting.

In Python 3.12, this limitation is removed because you can reuse


quotation marks:

Python
>>> # Python 3.12

18
>>> f"{
... f"{
... f"{
... f"{
... f"{
... f"Deeply nested f-string!"
... }"
... }"
... }"
... }"
... }"
'Deeply nested f-string!'
Before the new f-string implementation, there was no formal limit
on how many levels of nesting you could have. However, the fact
that you couldn’t reuse string quotes imposed a natural limit on
the allowed levels of nesting in f-string literals. Starting with
Python 3.12, you can reuse quotes, so there are no limits for
nesting f-strings.

Remove ads

Using Backslashes

Another limitation of f-strings before 3.12 is that you can’t use


backslash characters in embedded expressions. Consider the
following example, where you try to concatenate strings using the
newline (\n) escape sequence:

Python
>>> words = ["Hello", "World!", "I", "am", "a", "Pythonista!"]

19
>>> f"{'\n'.join(words)}"
File "<input>", line 1
f"{'\n'.join(words)}"
^
SyntaxError: f-string expression part cannot include a backslash
In this example, you get a SyntaxError because f-strings don’t
allow backslash characters inside expressions delimited by curly
brackets.

Again, the new f-string implementation that comes with Python


3.12 solves the issue:

Python
>>> # Python 3.12

>>> words = ["Hello", "World!", "I", "am", "a", "Pythonista!"]

>>> f"{'\n'.join(words)}"
'Hello\nWorld!\nI\nam\na\nPythonista!'

>>> print(f"{'\n'.join(words)}")
Hello
World!
I
am
a
Pythonista!
The new f-string implementation lifted the limitation of using
backslash characters in embedded expressions, so you can now
use escape sequences in your f-strings.

Writing Inline Comments

F-strings up to Python 3.11 don’t allow you to use the # symbol in


embedded expressions. Because of that, you can’t insert
comments in embedded expressions. If you try to do it, then you’ll
get a syntax error:

20
Python
>>> employee = {
... "name": "John Doe",
... "age": 35,
... "job": "Python Developer",
... }

>>> f"""Storing employee's data: {


... employee['name'].upper() # Always uppercase name before
storing
... }"""
File "<stdin>", line 3
}"""
^
SyntaxError: f-string expression part cannot include '#'
When you use # to introduce a comment in an f-string, you get
a SyntaxError. Fortunately, the new f-strings in Python 3.12 also
fix this problem:

Python
>>> # Python 3.12

>>> employee = {
... "name": "John Doe",
... "age": 35,
... "job": "Python Developer",
... }

>>> f"Storing employee's data: {


... employee["name"].upper() # Always uppercase name
before storing
... }"
"Storing employee's data: JOHN DOE"
Now you can add inline comments if you ever need to clarify
something in the embedded expressions of an f-string. Another
improvement is that you can add line breaks inside the curly
braces, similar to what you can do inside parentheses outside f-

21
strings. You don’t even need to use the triple-quoted multiline
strings to do this.

Deciphering F-String Error Messages

Python’s new PEG parser opens the door to many improvements


in the language. From the user’s perspective, one of the most
valuable improvements is that you now have better error
messages. These enhanced error messages weren’t available for
f-strings up to Python 3.11 because they didn’t use the PEG
parser. So, the error messages related to f-strings were less
specific and clear.

Python 3.12 came along to fix this issue, too. Take a look at the
following example, which compares the error message for an
incorrect f-string in both 3.11 and 3.12:

Python
>>> # Python 3.11
>>> f"{42 + }"
File "<stdin>", line 1
(42 + )
^
SyntaxError: f-string: invalid syntax

>>> # Python 3.12


>>> f"{42 + }"
File "<stdin>", line 1
f"{42 + }"
^
SyntaxError: f-string: expecting '=', or '!', or ':', or '}'
The error message in the first example is generic and doesn’t
point to the exact location of the error within the offending line.
Additionally, the expression is surrounded by parentheses, which
adds noise to the problem because the original code doesn’t
include parentheses.

In Python 3.12, the error message is more verbose. It signals the


exact location of the problem in the affected line. Additionally, the

22
exception message provides some suggestions that might help
you fix the issue.

In this specific example, the suggestions aren’t that useful


because they focus on an operator that’s possibly wrong.
However, having the exact location where the problem happened
gives you a strong clue. You have a missing operand in the
embedded expression.

Using Traditional String Formatting Tools Over F-Strings

Even though f-strings are a pretty cool and popular Python


feature, they’re not the one-size-fits-all solution. Sometimes the
modulo operator (%) or the .format() method provides a better
solution. Sometimes, they’re your only option. It all depends on
your specific use case.

In the following sections, you’ll learn about a few situations where


f-strings may not be the best option. To kick things off, you’ll start
with a use case that’s closely related to your code’s readability.
That’s when you want to interpolate values from a dictionary into
a given string.

Dictionary Interpolation

Interpolating dictionary values into a string may be a common


requirement in your code. Because you now know that f-strings
are neat, you may think of using them for this task. You end up
with a piece of code that looks like the following:

Python
>>> person = {"name": "Jane Doe", "age": 25}

>>> f"Hello, {person['name']}! You're {person['age']} years


old."
"Hello, Jane Doe! You're 25 years old."
That’s great! The code works just fine. However, it doesn’t look
clean because of all those dictionary key lookups embedded in
the string. The f-string looks cluttered and may be hard to read.
How about using the .format() method?

23
Here’s a new version of your code:

Python
>>> "Hello, {name}! You're {age} years old.".format(**person)
"Hello, Jane Doe! You're 25 years old."

>>> "Hello, {name}!".format(**person)


'Hello, Jane Doe!'
In this example, you use direct names instead of dictionary
lookups in the replacement fields. The only additional
requirement is that you need to use the dictionary unpacking
operator (**) in the call to .format(). Now, the string looks cleaner
and is also a bit shorter than the version using an f-string.

As an additional gain, it’s important to note that the number of


replacement fields in the string doesn’t have to match the
number of keys in the input dictionary. The .format() method will
ignore unnecessary keys.

You also have the option of using the modulo operator, though:

Python
>>> "Hello, %(name)s! You're %(age)s years old." % person
"Hello, Jane Doe! You're 25 years old."

>>> "Hello, %(name)s!" % person


'Hello, Jane Doe!'
This time, the string is even shorter. You use direct names in the
replacement fields and don’t have to use the dictionary unpacking
operator because the modulo operator unpacks the dictionary for
you. However, some may say that the replacement fields aren’t
that readable and that the modulo operator has limited formatting
capabilities.

24

You might also like