Formatting strings to include values of variables desirably is an important string operation that is crucial to any process involving string outputs.
Each programming language provides its own way of formatting strings. For example, the C language’s printf
function (which literally stands for ‘print formatted’) allows you to specify placeholders for integer, float, string, etc. values in the string.
You pass the actual values as additional parameters to the function.
Similarly, Python provides the format
method on strings, to specify placeholders for various data types and supply their values as arguments.
In this article, we will understand the motivation behind string interpolation and look at various methods for string interpolation in Python.
Table of Contents
Why do we need string interpolation?
Programming languages are dynamic in nature. Most real-world applications, while implementing any use-case, dynamically take into account the data coming from one of the many sources such as user-defined variables, user-provided input, file data, system variables, etc.
These values cannot be hardcoded into the program.
Whenever we construct strings, very often we might need to plug in such values at different places in the string. This plugging-in of data into the strings programmatically is called string interpolation.
Imagine you are given the task of creating a student dashboard whose homepage shows the student’s basic details such as name, roll no., semester, course name, etc., and academic summary such as subjects taken, exam results, semester-wise grades, aggregate GPA. etc.
You will naturally display this information in a nice formatted way using a string. And of course, this summary would look different for different students depending on their individual data points.
One way is to print each label describing the nature of information, followed by the corresponding value fetched from the database, and then repeat this sequentially for every attribute.
But this would significantly limit the nature of formatting to a very simple form that would become more prone to errors with an increasing number of attributes desired in the summary.
What if you could just define the entire summary template as a single string, with placeholders for the values of the respective student attributes, and use it for every student while plugging in the values for the target student?
Wouldn’t that make your life as a developer easier and make your result less prone to errors?
String interpolation methods are just the right choice for you to solve this kind of problem.
In a nutshell, string interpolation not only provides value for the end-user but also greatly enhances the developer experience.
A good developer experience would include constructing such complex and dynamic strings with a minimum line of code.
Python provides various string interpolation methods, some of which are more efficient than others while some are just easier to use than others.
Interpolation by concatenation
One of the most naive ways of doing string interpolation would be to append the values to the string using the concatenation operation on the string.
But this would mean repeatedly doing string concatenation as many times as the number of variables we want to plug in the string.
Not only would it increase the number of lines of code but would also increase the chances of making mistakes when constructing the string.
These problems would only become worse as the length of the string and the number of variables to be plugged in increase.
first_name = "Andrew" last_name = "Tate" roll_no = 61214000 course = "MSc" major = "Aerospace Engineering" semester = 3 cgpa = 8.9 output = "Welcome " + first_name + " " + last_name + ".\nPlease find below your personal and academic details:\n"+\ "Roll No. " + str(roll_no)+"\nCourse: "+course + " in " + major+"\n"+ "Semester: "+str(semester)+"\nCurrent CGPA: "+str(cgpa)+\ "\nIf you find any error in your details, please write to the administration at admin@harvard.edu" print(output)
Output:
Image may be NSFW.
Clik here to view.
As you might have figured out, this method involves too many concatenation operations.
Every time you want to switch between values stored in variables and plain string literals, you have to perform string concatenation.
Also notice that to add values of types other than strings i.e integers and floating-point numbers, you have to convert them into a string before using them. If you don’t do that you will get an error stating you cannot concatenate strings with other types.
As I already pointed out earlier, this is a naive way and significantly increases the scope of potential errors in your code.
So let’s look at a better way of doing string interpolation in Python i.e using the modulo operator %
.
Using the modulo operator
Normally, the modulo operator is used for an arithmetic operation. But in Python, it has been overloaded to perform string interpolation.
You create a string literal with placeholders at different positions for values of different data types.
This string literal is followed by the modulo operator and a tuple of actual values to be substituted in the positions of placeholders.
The placeholders (or conversion flags) inside the string literal are also specified using the modulo operator.
While many conversion flags can be used for specifying different value types, let’s look at some of the more popular ones.
An integer value can be specified with the flag ‘d’ (%d
to be more specific)
A floating-point number is specified as %f
or %F
.
It is also possible to another string using the flag %s
.
Let’s use these flags in our previous example.
first_name = "Andrew" last_name = "Tate" roll_no = 61214000 course = "MSc" major = "Aerospace Engineering" semester = 3 cgpa = 8.934 output = """Welcome %s %s. Please find below your personal and academic details: Roll No. %d Course: %s in %s Semester: %d Current CGPA: %f If you find any error in your details, please write to the administration at admin@harvard.edu """%(first_name, last_name,roll_no, course, major, semester, cgpa) print(output)
Output:
Image may be NSFW.
Clik here to view.
Now that one looks much neater! You don’t have to convert any non-string value to a string, and you have to define just one string literal instead of multiple for each part as was the case in concatenation.
But notice that the CGPA value, which is a floating point number, has a lot of trailing zeroes.
This is because by default, %f
will substitute values with precision 6.
Even if you passed an integer in place of the %f
flag, it will still add a decimal point with 6 trailing zeroes!
This is certainly not desirable and can put off your users.
However, there is a solution to this.
You can additionally specify the precision for floating point values or reserve a fixed length space for the value with an integer specified before the conversion flag.
For example, to specify a precision of 3 digits after the decimal, you can use the flag %.3f
.
To reserve a space of 10 characters for an integer value you can use %10d
. Similarly, for strings, you can use %10s
.
If the actual value has a length less than the specified reserved length (in this case 10), leading whitespaces would be introduced of the remainder length.
s = "Hi There! My name is Mr. %10s, I am %10d years old. I have %.2f dollars"%("Nasser", 17, 52.6789) print(s)
Output:
Image may be NSFW.
Clik here to view.
Notice the leading whitespace of length 4 for the string “Nasser”(length=6), and the whitespace of length 8 for age 17(length=2).
Also, the precision of the floating value (52.6789) is now 2 (with the value being rounded to the nearest precision).
You could also reserve the whitespace for the floating point value with say, %10.2f
.
This will indicate a length of 10 and a precision of 2.
Also, if you want trailing whitespaces instead of the default leading ones, you can specify the lengths with a negative sign.
If you have just one value to inject into the string, you can skip the tuple, and directly specify the value (eg. "I am %d years old"%17
)
Most Importantly, The order of the values being passed in the tuple corresponds to the order of the conversion flags in the string, so always ensure the order of the arguments is correct.
Also, ensure that the number of conversion flags is equal to the number of operands you pass to the modulo operator.
You can also specify the values using a dictionary instead of a tuple, where you will insert the dictionary keys(enclosed in parentheses) between the modulo (%
) and the conversion flags. The corresponding values of these keys would be substituted in the string at the respective positions.
An advantage of using dictionaries instead of tuples is that you don’t have to worry about the order of the values inside the dictionary because dictionaries are anyway unordered mappings in nature.
output = """Welcome %(first_name)s %(last_name)s. Please find below your personal and academic details: Roll No. %(roll_no)d Course: %(course)s in %(major)s Semester: %(semester)d Current CGPA: %(cgpa).2f If you find any error in your details, please write to the administration at admin@harvard.edu """%{"cgpa" : 8.934, "first_name" : "Andrew", "last_name" : "Tate", "roll_no" : 61214000, "course" : "MSc", "major" : "Aerospace Engineering", "semester" : 3} print(output)
Output:
Image may be NSFW.
Clik here to view.
Using the format method
Python 3 introduced a new method on strings for string formatting. This is the str.format
method.
It makes string interpolation much easier and also overcomes some of the issues of the modulo operation (such as incorrect display of tuples and dictionaries, etc.)
The format
method is quite similar in usage to the modulo operator.
Instead of using the conversion flags in the string, you specify braces. You pass the actual values as arguments to the format method.
One of the most important and evident advantages is that you don’t have to remember the different conversion flags for each value type.
However, you can still optionally specify them if you want, for eg. to specify precision.
first_name = "Andrew" last_name = "Tate" roll_no = 61214000 course = "MSc" major = "Aerospace Engineering" semester = 3 cgpa = 8.934 output = """Welcome {} {}. Please find below your personal and academic details: Roll No. {} Course: {:s} in {} Semester: {} Current CGPA: {:.2f} If you find any error in your details, please write to the administration at admin@harvard.edu """.format(first_name, last_name,roll_no, course, major, semester, cgpa) print(output)
Output:
Image may be NSFW.
Clik here to view.
Notice how I could optionally specify the conversion flag for the course and also specify precision for the CGPA which is a floating point number.
If you didn’t specify any conversion flag, however, by default, the values are converted to a string. So there is no default precision of 6 for floating point values.
By default, the order of the values being substituted is the same as the order of the arguments passed to the format
method.
You can optionally specify the index of arguments inside the braces to overcome this default order. You can also pass named arguments to the format method, and use the names inside the corresponding braces in the string.
first_name = "Andrew" last_name = "Tate" roll_no = 61214000 course = "MSc" major = "Aerospace Engineering" semester = 3 cgpa = 8.934 output = """Welcome {1} {3}. Please find below your personal and academic details: Roll No. {4} Course: {2:s} in {5} Semester: {sem} Current CGPA: {0:.2f} If you find any error in your details, please write to the administration at admin@harvard.edu """.format(cgpa, first_name, course, last_name,roll_no, major, sem=semester ) print(output)
Output:
Image may be NSFW.
Clik here to view.
Notice how I combined some of the indices with conversion flags, separated by the colon (:
)
Also, notice the usage of a keyword argument ‘sem’ for the semester and its specification in the corresponding brace in the string.
Another advantage of using the format
method is that you don’t need to specify the exact number of arguments as the number of placeholder braces if you use indexing or named references for the arguments.
Let’s look at a simple example demonstrating this.
count = 8 s = "The guy's name is {name}. {name} loves fitness.\nHe runs {0} kilometers a day, eats {0} bananas and sleeps for {0} hours a day. {name} credits his love for fitness to his footballer father {1}.".format( count, "Christiano Ronaldo", name = "Mateo") print(s)
Output:
Image may be NSFW.
Clik here to view.
Here we used 7 placeholder braces but passed only 3 arguments.
Note that you should not use the combination of empty braces and argument/name specification as these will lead to an error.
Needless to say, you can specify the values for the placeholders using a dictionary and pass them to the format
method using the double star operator **
to unpack the dictionary as keyword arguments.
We can also add leading and trailing whitespaces using the <
and the >
operators respectively with the respective conversion flags.
Additionally, you can add both leading and trailing whitespaces together to center align the value using the ^
operator.
You can optionally specify the fill character to be something other than the whitespace.
print("The king is back.His name is\n{:^25}\n{:^25}\n\n".format("Leo", "King Leo")) print("Log dir:\nOpened file:{:<15},last modified at {:>15}\n{end_msg:*^30}".format("abc.txt","00:02h",end_msg="End of file"))
Output:
Image may be NSFW.
Clik here to view.
In the first string, we used center alignment using the ^
operator.
In the second string, we added left, right, and center alignment. In the last case, we also specified the fill character to be ‘*’ instead of space.
We can use a comma as a thousand separator when plugging in large integer numbers using the format
method.
For datetime
objects, we have additional format specifiers.
large_no = 74937934973497 print(f"In America {large_no} would be written as {large_no:,}".format( large_no=large_no )) from datetime import datetime now = datetime.now() print("I am running this code on {dt:%d-%m-%Y} at {dt:%H:%M:%S}".format(dt = now))
Output:
Image may be NSFW.
Clik here to view.
Template strings in Python
Python’s string
module provides a Template class which is used to create a string template with named placeholders for values.
We can substitute actual values in place of these placeholders by calling the substitute
method on the template object, and passing values for each placeholder either as keyword arguments or as dictionaries.
By default, the placeholder is identified using the $
delimiter. The placeholder identifiers are thus specified as $identifier
or ${identifier}
.
from string import Template template = CustomTemplate("""Welcome $fname $lname. Please find below your personal and academic details: Roll No. $roll_no Course: $course in $major. Semester: $sem Current CGPA: $cgpa If you find any error in your details, please write to the administration at admin@harvard.edu """) first_name = "Andrew" last_name = "Tate" roll_no = 61214000 course = "MSc" major = "Aerospace Engineering" semester = 3 cgpa = 8.934 output = template.substitute(fname = first_name, lname = last_name, roll_no = roll_no, course = course, major = major, sem = semester, cgpa = cgpa) print(output)
Output:
Image may be NSFW.
Clik here to view.
You can also specify a custom delimiter by defining a custom class inheriting the Template
class and overriding the delimiter
attribute.
One disadvantage of using template string for interpolation is that you cannot use format specifiers or conversion flags if you needed very specific formatting or a certain precision for decimal values like in the format
method or using the modulo operator.
Using f-strings
Python 3.6 introduced us to a new, more elegant, more concise, and faster way of string interpolation called the ‘f-strings’.
‘f-strings’ stands for ‘Formatted String literals’ and is arguably the most preferred choice for string interpolation today.
f-strings allow us to directly inject Python variables, literals, and expressions into a string without calling any additional method or using the modulo operator.
They are normal strings but with the prefix f
or F
.
The values to be injected are directly specified using the curly braces {val}
.
f-strings also provide options to specify the type and width of the passed expression.
Let’s take our example of Mr. Tate and revamp it using f-strings!
first_name = "Andrew" last_name = "Tate" roll_no = 61214000 course = "MSc" major = "Aerospace Engineering" semester = 3 cgpa = 8.934 output = f"""Welcome {first_name} {last_name}. Please find below your personal and academic details: Roll No. {roll_no} Course: {course:s} in {major:25s}. Semester: {semester} Current CGPA: {cgpa:.2f} If you find any error in your details, please write to the administration at admin@harvard.edu """ print(output)
Output:
Image may be NSFW.
Clik here to view.
So far we passed variables. But f-strings can be used to specify literally any Python expression in the string!
import math diameter = 2 print(f"Radius of the circle is {diameter/2} cm and its area is {math.pi*math.pow(diameter/2, 2):.4f} sq.cm ")
Output:
Image may be NSFW.
Clik here to view.
Notice the use of arithmetic expressions, constants, methods, and precision specifications in a single f-string!
If you have a long f-string, that you don’t want to specify all in a single line, but span over multiple lines you can break them into multiple f-strings. You may have noticed that in all our examples of Mr. Tate, we specified our long string using triple quotes """ """
.
Triple quotes format your string by adding all the whitespaces(newlines, tabs, spaces) enclosed within the quotes.
This may create conflict with any default indentation provided by your code editor and may produce undesired results.
So then how do we produce multiline f-strings? Well, you can just use multiple f-strings!
You can either enclose the whole set of strings inside parentheses with each f-string on a new-line, or you can just skip the parenthesis and encode the newline with an escape character at the end of each f-string.
Also, since we specify expressions using the curly braces, what if we wanted to include an actual curly brace in our f-string literal?
You can do this by enclosing curly braces inside curly braces {{}}
What if we want to have double quotes in our f-string which itself is specified by double quotes?
You can ‘escape’ the quote using the backslash character ‘\
‘.
Let’s use all these ideas in our running example.
first_name = "Andrew" last_name = "Tate" roll_no = 61214000 course = "MSc" major = "Aerospace Engineering" semester = 3 cgpa = 8.934 output1 = (f"Welcome \"{first_name} {last_name}\".\n" f"Please find below your personal and academic details:\n" f"Roll No. {roll_no}") output2 = f"Course: {course:s} {major}{{Hons}}\n"\ f"Semester: {semester}\n"\ f"Current CGPA: {cgpa:.2f}\n"\ f"If you find any error in your details, please write to the administration at admin@harvard.edu" print(output1) print(output2)
Output:
Image may be NSFW.
Clik here to view.
We have broken a long f-string into multiple strings using parenthesis in output1 and escaping return(enter) with \
in output2.
We also escaped the double quotes in the first f-string of output1 and included actual curly braces in our string in the first f-string of output2.
String interpolation in JSON
Python provides the json
module which offers methods to convert to and from JSON strings.
JSON is a widely used format for communication over APIs.
We could store the desired JSON format as a string with required placeholders and substitute values dynamically into this template before sending it further.
We can achieve this both using f-strings and the format
method, but I would suggest going for the format method as long JSON templates could be stored in files or databases which can only be read as normal Python strings instead of f-strings.
Be careful that JSON’s string conversion methods require the data in the string to be in JSON format which involves curly braces.
So you should escape these curly braces. We already discussed how to do this in a previous section!
import json user_template = '{{"user_id":{0}, "personal_details":{{"first_name": "{1}", "last_name":"{2}", "zipcode":{3}, "email":"{4}"}}, "balance":{5:.2f}}}' uid = 5980 fname = "Max" lname = "Monroe" zipcode = 419782 email = "max@hotmail.com" bal = 422345.78 response_data = json.dumps(json.loads(user_template.format(uid, fname, lname, zipcode , email, bal)), indent=3) print(response_data)
Output:
Image may be NSFW.
Clik here to view.
I hope you find the tutorial useful. Keep coming back.