Python f-Strings and String Formatting: A Practical Guide
Python f-strings let you put values, expressions, and formatting rules directly inside a string. They are the clearest and most common way to build readable text from variables in Python 3.12 and later. This article shows how f-strings work, how formatting specs change the output, and where people often make mistakes.
Difficulty: Beginner
Helpful to know first: basic Python variables, strings, and how expressions like arithmetic and function calls work.
How Python f-Strings Turn Values Into Text
An f-string is a string literal that starts with f or F. Inside the string, anything inside curly braces is evaluated as Python code, then turned into text and inserted into the final string.
The main job of an f-string is to save you from manual string joining and repeated calls to str(). Instead of building a sentence in pieces, you write the sentence in its final shape and let Python fill in the values.
Here is the smallest useful example:
name = "Mina"
age = 20
print(f"{name} is {age} years old.") # Output: Mina is 20 years old.Python reads the string, finds {name} and {age}, evaluates those names, converts their values to text, and inserts them into the result.
That means an f-string is not a special formatting function you call later. It is processed when Python creates the string object.
Worked Examples of Python f-Strings and Formatting
Using expressions inside braces
You do not need to store every result in a temporary variable. Any valid Python expression can go inside the braces.
price = 12.5
quantity = 4
print(f"Total: {price * quantity}") # Output: Total: 50.0The expression price * quantity is evaluated first. Then Python converts the numeric result to a string and inserts it into the sentence.
This is useful for calculations, function calls, and short transformations that make the output easier to read.
Formatting decimals and currency-like values
Raw floating-point numbers often show more digits than you want. A format specifier after a colon lets you control how the value appears.
tax_rate = 0.075
subtotal = 49.99
tax = subtotal * tax_rate
print(f"Tax: {tax:.2f}") # Output: Tax: 3.75The .2f format means “show this as a fixed-point number with 2 digits after the decimal point.” This is one of the most common formatting tasks in Python.
If you need correct financial rounding, remember that formatting only changes how a number is displayed. It does not fix floating-point representation issues by itself.
Aligning text in columns
F-strings can pad values so output lines up in neat columns. This is useful in console tables, logs, and simple reports.
product = "Notebook"
price = 4.5
print(f"{product:<12}{price:>8.2f}") # Output: Notebook 4.50In this example, <12 left-aligns the product name in a field 12 characters wide, and >8.2f right-aligns the price in a field 8 characters wide with 2 decimal places.
The result is easier to scan than text built with manual spaces.
Using f-strings with dictionaries and attributes
Because the inside of {} is normal Python code, you can access dictionary keys and object attributes directly.
student = {"name": "Ava", "score": 91}
print(f"{student['name']} scored {student['score']} points.") # Output: Ava scored 91 points.This keeps the output logic close to the data instead of spreading it across several concatenated strings.
When you work with objects, the same idea applies to attributes:
class User:
def __init__(self, name, points):
self.name = name
self.points = points
user = User("Leo", 18)
print(f"{user.name} has {user.points} points.") # Output: Leo has 18 points.F-strings do not need special syntax for dictionaries or attributes. They simply evaluate the expression you write between the braces.
Escaping braces when you want literal curly braces
If you want an actual { or } in the output, you must double it. Single braces are reserved for expressions.
print(f"Set notation looks like {{1, 2, 3}}") # Output: Set notation looks like {1, 2, 3}This is important when printing templates, configuration text, or any format that uses braces as part of the content.
How Python Applies Formatting Rules Inside f-Strings
The part after the colon inside a placeholder is called a format specifier. It tells Python how to convert the value into a string. The specifier is not the same thing as the expression itself.
For example, compare these two forms:
value = 3.14159
print(f"{value}")
print(f"{value:.2f}") # Output: 3.14159\n3.14The first line uses the default conversion. The second line uses a formatting rule that rounds the display to two decimal places.
The format mini-language can also add signs, zero padding, grouping, and alignment. For example:
n = 42
print(f"{n:06d}") # Output: 000042
print(f"{n:>6}") # Output: 42
print(f"{n:<6}") # Output: 42 Here, 06d pads an integer with leading zeroes to width 6, while >6 and <6 control alignment.
Formatting is especially useful when you want predictable output for logs, reports, filenames, or user-facing text.
Why f-Strings Are Different From Concatenation and format()
Before f-strings, many beginners used string concatenation or str.format(). Both still work, but f-strings are usually easier to read and write.
| Approach | Example | What it feels like |
|---|---|---|
| Concatenation | "Hello " + name | Simple, but awkward once numbers or formatting are involved |
| format() | "Hello {}".format(name) | Flexible, but more verbose |
| f-string | f"Hello {name}" | Short, readable, and usually easiest to maintain |
For example, this is hard to read with concatenation:
name = "Nina"
score = 9
print("Hello " + name + ", score = " + str(score)) # Output: Hello Nina, score = 9The f-string version is shorter and easier to read:
print(f"Hello {name}, score = {score}") # Output: Hello Nina, score = 9That readability advantage becomes more important as the sentence gets longer or includes formatting rules.
How f-Strings Behave in Detail
F-strings are evaluated left to right, and each placeholder is processed as part of building the string. If an expression has side effects, those side effects happen during string creation.
def next_value():
print("called")
return 7
print(f"Value: {next_value()}")
# Output:
# called
# Value: 7This matters because the expression is not delayed. It runs immediately when the f-string is created.
Another detail: f-strings can contain almost any expression, but they cannot contain statements. You can call a function, index a list, and do arithmetic, but you cannot write a for loop or assignment inside the braces.
Also, an f-string is not magical once it is built. After evaluation, it is just a regular Python str object.
An f-string is often fastest to write and easiest to read, but that does not mean it is always the best choice for every formatting job. The best choice still depends on whether the output is simple text, repeated templates, or data structured for later use.
Predict the Output
Predict the value after formatting and arithmetic
What does this print?
x = 5
y = 2
print(f"{x / y:.1f}")Show answer
It prints 2.5.
The expression x / y uses true division, so 5 / 2 becomes 2.5. The format specifier .1f keeps one digit after the decimal point, so the final text is 2.5.
Predict the output with doubled braces
What does this print?
name = "Ada"
print(f"{{name}} = {name}")Show answer
It prints {name} = Ada.
Double braces become a literal brace pair in the output. The placeholder {name} is still evaluated, so Python inserts the value Ada after the literal text.
Common Mistakes With Python f-Strings
Forgetting the f prefix
Without the prefix, Python treats the string as ordinary text. The braces stay in the result instead of being evaluated.
Problem: The string looks like an f-string, but it is missing the prefix, so the placeholder is never evaluated.
name = "Sam"
print("Hello {name}")Fix: Add f before the opening quote.
name = "Sam"
print(f"Hello {name}")The fixed version works because Python now evaluates the expression inside the braces.
Using one brace when you meant a literal brace
Single braces start an expression. If you want the character itself, you must double it.
Problem: Python tries to parse {1, 2, 3} as an f-string expression, which is not what the author intended for literal text.
print(f"Set: {1, 2, 3}")Fix: Double the braces around any literal brace pair.
print(f"Set: {{1, 2, 3}}")The corrected version works because doubled braces escape to literal braces in the output.
Putting the format specifier in the wrong place
The colon belongs inside the placeholder, after the expression. If you place it outside, Python does not read it as formatting.
Problem: The format rule is written outside the braces, so Python cannot apply it to the value.
price = 3.5
print(f"{price}.2f")Fix: Put the format specifier after a colon inside the braces.
price = 3.5
print(f"{price:.2f}")The corrected version works because :.2f is attached to the expression being formatted.
Trying to assign inside the braces
F-strings are for expressions, not statements. Assignment is not allowed inside a placeholder.
Problem: The placeholder contains an assignment, which Python does not allow inside an f-string expression.
count = 3
print(f"Count: {count = 4}")Fix: Assign the value before the f-string, then format the variable.
count = 4
print(f"Count: {count}")The fixed version works because the placeholder now contains a valid expression that simply reads the variable.
Find the Bug
A hurried developer wrote this code to print a receipt total. It looks close, but one part is wrong. Find the bug.
item = "Pen"
price = 1.5
qty = 3
print(f"{item}: ${price * qty}.2f")Show answer
The bug is the placement of .2f. It is outside the braces, so Python treats it as plain text instead of a format specifier.
The intended output is a price formatted to two decimal places.
Fixed code:
item = "Pen"
price = 1.5
qty = 3
print(f"{item}: ${price * qty:.2f}") # Output: Pen: $4.50The fixed version works because the format specifier is now attached to the expression inside the braces.
When to Use f-Strings and When to Use Something Else
F-strings are the best default choice for readable string formatting in Python. They are especially good when you already know the values you want to insert and you want the output to stay close to the code that builds it.
| Use this | When it fits best | Why |
|---|---|---|
| f"...{x}..." | Most everyday formatting | Clear, short, and easy to read |
| str.format() | Reusable templates or older code | Useful when a template is built once and filled later |
| string.Template | Very simple named placeholders or safer substitution in some text templates | Uses a different placeholder style and can be easier to limit |
| Concatenation | Very small pieces of text | Fine for tiny examples, but grows messy fast |
Use an f-string when the text is built right where the values are known. Use a different tool when the template must be stored, reused, or handled in a more structured way.
One important comparison is f"..." versus format(). The practical difference is mostly readability. Both can format numbers, align text, and insert values. F-strings usually win because they let you read the sentence in the same order it appears in the code.
Interview Questions About Python f-Strings
Why are f-strings preferred over concatenation?
They are easier to read, reduce repeated str() calls, and let you place expressions and formatting rules close to the text they affect. That makes code shorter and less error-prone.
Can an f-string contain any Python code?
It can contain many expressions, including arithmetic, indexing, attribute access, and function calls. It cannot contain statements like assignments, for loops, or def blocks inside the braces.
What is the difference between {x} and {x!r}?
{x} uses the normal string form of the value. {x!r} uses repr(), which is more detailed and often useful for debugging. If you are printing user-facing text, plain {x} is usually better.
Try It Yourself
Write a small program that prints a neatly formatted receipt line.
- Create variables for an item name, unit price, and quantity.
- Compute the total price.
- Print one line in the form Item: $total.
- Format the total with two decimal places.
Expected output:
Tea: $7.50Hint: Multiply the price by the quantity inside the f-string, then attach :.2f to the expression.
Show solution
item = "Tea"
unit_price = 2.5
quantity = 3
total = unit_price * quantity
print(f"{item}: ${total:.2f}") # Output: Tea: $7.50Summary
- Python f-strings insert expressions into text using curly braces.
- They are usually the most readable way to format strings in Python 3.12+.
- Use a colon inside the braces to add formatting rules like width, alignment, and decimal places.
- Double braces when you need literal { or } characters in the output.
- F-strings evaluate expressions immediately and produce a normal str.
- Use f-strings for everyday formatting, and reach for other tools only when a reusable template or special placeholder system is a better fit.
Next, learn how format() and the format mini-language let you control padding, alignment, and numeric presentation in more detail.