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.62Here, : is used to separate the value being formatted (latency) and the "format specifier" (.2f)
In .2f format specifier:
- .indicates that we are setting precision
- 2is the precision
- findicates 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.6193Scientific 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+01E 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 peopleYou 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,000Aligning 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 rowThe 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=28Order 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! 👋
