Why is there an f before this string? An introduction to f-strings and string formatting
How do I add my variable into a string in python? If I have someones name, how do I print this back out, with additional information, like a hello message?
define String Formatting
noun
The fancy way of saying making strings/text look the way you want them to, whether that's with variables, new lines, spaces, capital letters etc.
Example: "The string formatting on the output of this simple command line app is great, it has colours and everything!"
String formatting has come a long way in the last few years. Let's take a quick trip down memory lane and look at the old ways of formatting strings, scroll to the bottom to see some f-string examples.
1. Starting from basics.
In python, everything is an object, this includes strings. This provides additional flexibility, eg: "a" * 4 == "aaaa"
, that is, you can 'concatenate' strings through addition and multiplication.
In the good ol' days, this is how we used to do it. Let's take a classic python2 hello world application for example.
#!/usr/bin/env python
name = input("What's your name? ")
print "Hello " + name
In this example we are using the most basic version of string formatting available. Concatenating (adding) two strings with the +
symbol.
If you save this code into hello_name.py
and run python3 hello_name.py
, you'll see the output as Hello Henry
, with Henry replaced by whatever you type in.
This is great for basic scripts, and makes sense when reading it, but what if we want to do something like "Hello " + name + ", looks like the weather will be " + weather_temp + " " + weather_scale + " degrees today."
. This is still possible, but definitely not very easy to read.
What if we want to take a number like 123.45678
and print it as $123.46
? This method is clearly quite limited when it comes to string formatting.
2. The magic of %.
So let's go back to our example. Say we have a float (a number with decimals, as in, not an integer), and we want to print it as a currency, with commas and rounding. How do we do this?
amount = float(input("How much? ")) # Convert input (str) to float
print("This much? " + amount)
If we input 1234.56789
, we get back "This much? 1234.56789"
. So how do we print this as an amount of dollars and cents?
We can use something called the "string formatting operator", or %
. Instead of the adding the string at the end, we can just place a % symbol, followed by a type (like d for integer, f for float, s for string etc.) and then follow this with the variable to replace. This provides for (slightly) more readable code.
Our example above would then be:
amount = float(input("How much? ")) # Convert input (str) to float
print("This much? %f" % amount)
But this still doesn't help us convert 1234.56789
to $1,234.57
. To do this we need to use "conversion flags". For example, if we are formatting a float, we can replace %f
with %.2f
, and this will round the float to 2 decimal places.
amount = float(input("How much? ")) # Convert input (str) to float
print("This much? $%.2f" % amount)
So now our output is $1234.57
, but we still don't have the comma for the thousand seperator...
3. You can't spell 'string formatting' without format!
So everything in python is an object, objects can have methods (functions) so why don't we just add a method to the string object to help us format strings?
Welcome string.format()
This was a big leap forward for python. We didn't just gain a more readable way of formatting strings (more on this in footnotes), we also gained more complex formatting rules.
So let's go back to our example. Say we have a float (a number with decimals, as in, not an integer), and we want to print it as a currency, with commas and rounding. How do we do this?
amount = float(input("How much? ")) # Convert input (str) to float
print("This much? ${0:,.2f}".format(amount))
Our ouput is now This much? $1,234.57
, but how? So the basic syntax of format is that it takes the first argument (value after the brackets, so .format(first, second) has two arguments, "first" and "second"), and places this in the first index (denoted by 0), so x = 123
, "The number is {0}".format(x)
returns The number is 123
.
The ,
tells python to format the number with commas, so 1200000 becomes 1,200,000 (if you're building an international application, remember that in some countries the , and . symbol are reversed).
Okay, so .format can do everything we want. Then why do you like f-strings so much?
4. The real reason f-strings will change your life
Despite common perceptions, development isnt (and shouldn't be) about getting the job done. It's about getting the job done well, and in a way that future developers will be able to understand and extend.
String.format has all the functionality you need. Python 2.7 has all the functionality you need. In fact, C has all the functionality you need, to do almost any task. When python upgraded to version 3, it made some radical, breaking changes to the language, and while some were for performance and some were for functionality, my personal favourite, f-strings, was primarily for robustness, readability and adaptability.
F-strings don't introduce any new functionality, but they drastically increase readability & adaptability (they also increase performance, but this to me is a sidenote).
Let's take a simple example first:
name = "Jenny"
company = "Acme Inc."
print(f"Hi {name}, welcome to {company}.")
>>> 'Hi Jenny, welcome to Acme Inc'
By simply adding an f before the string, we've been able to input any variable we have in the current scope, and print it in a string. This isn't really revolutionary, but what is revolutionary is how easy it is to understand.
In todays world code isn't written once to perform a function, it's written, tested, reviewed, modified, extended, and one day maybe replaced. Readability is key to this, and f-strings have provided a massive jump in readability. If you knew nothing about python at all, and you looked at the previous example, I would bet you could guess what the output looked like. Never underestimate how valuable readability is. With our simple examples it seems almost too easy, but let's look at a more complex example, making an SQL query.
col1 = 'bar'
col2 = 'baz'
query = f'SELECT * FROM foo WHERE {col1} = ? AND {col2} = ?'
cursor.execute(query, params)
This query returns all rows where the values in columns "bar" and "baz" are equal to the values set in "params". This isn't as exciting, but much more valuable, and more likely in an enterprise setting. If we rewrite this with a %
operator, or a .format()
function, it quickly becomes more difficult to read.
The formatting available to f-strings is similar to .format, so I won't include it in this document, but I hope I've convinced you that f-strings are valuable. For testing, readability, extensibility and more.
Fun Facts
-
You can use either a lowercase, or a capital F, and the string formatting will be the same.
f"The result is {result}"
is the same asF"The result is {result}"
. -
If you want to format strings over multiple lines, you can either put an f before each line with a variable:
print(
f"Hi {name}"
"welcome to my post"
f"you're {num_days} late"
)
Or, you can simply use python's multiline strings (denoted by triple quotes, or tripe double quotes):
print(
f"""Hi {name}
welcome to my post
you're {num_days} late"""
)
Clever Facts
- String formatting is stackable, that is, you can format strings within strings. Say you want a variable number of decimal points
decimals = 3
amount = float(input("How much? ")) # Convert input (str) to float
print(f"This much? ${amount:,.{decimals}f}")
With input of 12312.123123
, the output of this script is This much? $12,312.123
.
Note: Please don't do this, this is really difficult to read.
- Say you want to use a dict key within a f-string, or any other quotation mark, you can safely do this, if you correctly alternate between
"
and'
.
Eg.
data = {"x": 123}
print(f'The result is {data["x"]}')
or equivalently:
data = {"x": 123}
print(f"The result is {data['x']}")
Follow me on twitter (@hjegeorge) or Medium for more posts, and message me if you'd like to learn more about python, data science, enterprise web applications, devOps, or anything else!
But actually, f’string have some limits. Somtimes we may write a general sql command, and the same position would be replaced by different variables. We can’t change the variable name every time to fit the f’string format. In such a case, .format is a better choice.
I think I understand what you’re saying, but could you provide an example where this would be used? I don’t see why .format makes this any easier.