Welcome to the first Python Tidbit, a series of brief weekly digests summarizing a Python feature or concept.
I will be posting these tidbits every Wednesday at 12:00 pm UTC so stay tuned for those special Wednesday wisdoms! 🚀
The topic of our first tidbit are f-strings!
f-strings, an acronym for format strings, are used to conveniently format and "style" complex string literals.
In their simplest and most common form, they can be used to plug in a variable's value in a string literal.
name = "Jake"
print(f"Hi, my name's {name}!")
Outputs:
Hi, my name's Jake!
... but f-strings are WAY more than just that! Let us see some of the cooler features of f-strings.
Rounding and Precision
A very common practice when dealing with floating point numbers is rounding them, adjusting decimal places, significant figures, etc.
f-strings make that process much simple:
Decimal Places
The f-string in the following code rounds the given number to 2
decimal places.
latency = 30.619325
print(f"Server latency is {latency:.2f} ms") # Server latency is 30.62
Here, :
is used to separate the value being formatted (latency
) and the "format specifier" (.2f
)
In .2f
format specifier:
.
indicates that we are setting precision2
is the precisionf
indicates that the formatted number should be a float.
Significant Digits
Like f
is used for decimal places, g
format specifier can be used to format a number to given number of significant digits.
latency = 30.619325
print(f"Server latency is {latency:.2g} ms") # Server latency is 31
print(f"Server latency is {latency:.6g} ms") # Server latency is 30.6193
Scientific Notation
Another useful format specifier is e
which formats the number in scientific notation with given number of digits after decimal.
latency = 30.619325
print(f"Server latency is {latency:.2e} ms") # Server latency is 3.06e+01
E
is another variant of e
that uses capital E
in resulting string and uppercases inf
to INF
for very large numbers.
Percentages
The %
format specifier shows the given number in percentage format. That is, multiply the given number with 100 and append a % sign.
probability = 0.489
print(f"Chances of rain: {probability:%}") # Chances of rain: 48.900000%
Of course, a more human output would set a precision as well:
probability = 0.489
print(f"Chances of rain: {probability:.2%}") # Chances of rain: 48.90%
Grouping
We can easily comma separate large numbers using f-strings.
world_population = 8062000000
print(f"The world poulation right now: {world_population:,} people")
Outputs:
The world population right now: 8,062,000,000 people
You could use _
as separator as well instead of ,
.
For floating point numbers, you might want to provide a precision of zero in some cases to strip of the .0
part:
world_population = 8.062e9
print(f"Without precision: {world_population:,}")
print(f"With precision: {world_population:,.0f}")
Gives the output:
Without precision: 8,062,000,000.0
With precision: 8,062,000,000
Aligning Strings
We are not just limited to numbers. We can format strings as well. More specifically, f-strings provide an easy way of aligning strings and setting widths.
Minimum Width
To begin with, we can constrain a string to a minimum width (length).
>>> string = "Hello"
>>> print(f"{string:4}")
'Hello'
The string was returned unchanged because it is already of width of 5 which is greater than provided 4
width.
Now, check this out:
>>> print(f"{string:8}")
'Hello '
Three spaces were automatically appended at the end of the string to ensure it is of width 8.
Alignment
We can actually control the direction in which spaces are added, or in other words, the string is aligned. For this, we use >
(right align), <
(left align), or ^
(center align):
>>> print(f"{string:<10}") # right align (default)
'Hello '
>>> print(f"{string:>10}") # left align
' Hello'
>>> print(f"{string:^10}" # center align
' Hello '
Fill Character
Last but not the least, we can change the character that is used to fill instead of blank space. It is provided (without any quotes) before the alignment specifier.
>>> heading = "Section 1"
>>> print(f"{heading:.^20}") # center align and fill with dot character
.....Section 1......
An example usage of string formatting is effortlessly crafting complex tables for showcasing information in the terminal. See the following code, for example:
# (name, color, unit price, quantity)
fruits_info = [
("Apple", "Red", 1.31, 5),
("Banana", "Yellow", 0.98, 8),
("Peach", "Orange", 2.40, 3),
("Pear", "Green", 6.73, 1),
]
# Print the header of table
header = ("Fruit Name", "Color", "Unit Price", "Quantity", "Total Price")
for item in header:
print(f"{item:^15}", end="")
print() # create newline for rows
# Print each row.
for name, color, unit_price, quantity in fruits_info:
price = unit_price * quantity
print(f"{name:^15}", end="")
print(f"{color:^15}", end="")
print(f"{unit_price:^15.2f}", end="")
print(f"{quantity:^15}", end="")
print(f"{price:^15.2f}", end="")
print() # create newline for next row
The output of the code above:
Fruit Name Color Unit Price Quantity Total Price
Apple Red 1.31 5 6.55
Banana Yellow 0.98 8 7.84
Peach Orange 2.40 3 7.20
Pear Green 6.73 1 6.73
... beautiful! 😘👌
Debugging
Before I end, I want to talk about a simple yet so valuable feature when it comes to debugging complex code.
We can print a variable's name alongside its value using a simple syntax of appending =
in front of variable name.
This allows for easy inspection of variables:
import datetime
a = 2
b = 3
created_at = datetime.datetime(2024, 3, 28)
print(f"{a=} {b=} {created_at.day=}")
Outputs:
a=2 b=3 created_at.day=28
Order and Type of Format Specifiers
The ordering of format specifiers matters. For example, you cannot provide precision specifier before the alignment and fill character specifiers.
To summarize, the following order is followed for format specifiers:
[fill][alignment][width][grouping][precision]
Furthermore, there are format specifiers that are data type specific. For example, .2f
(decimal places) is not supported on non-float types, obviously.
There are other components of f-strings that were not covered in this article and are not included in the order description above.
Conclusion
Of course, there's a lot more to say on f-strings but I don't want to turn this tidbit into an entire book chapter!
Interestingly, there's actually a "mini language" for manipulating formats in Python that defines all the format specifiers we have seen above along with many others.
Read more about it in Python's Documentation!
Got something to say? Feel free to comment on this article and share what you think about f-strings!
I'll see you next Wednesday with another cool Python feature. Ciao! 👋