Previous | Tutorial index | Next
Construct and format strings with the f prefix and the format method.
String formatting is the art of injecting dynamic values—variables, expressions, and calculations—into static text. It is essential for generating user‑friendly output, building log messages, creating reports, and preparing data for storage or transmission. While you could concatenate strings with + (e.g., "Hello " + name + "!"), this quickly becomes messy, error‑prone, and inefficient for complex outputs.
This tutorial focuses on the two modern approaches to string formatting in Python:
.format() method – introduced in Python 3.0, it is powerful, flexible, and still widely used, especially in codebases that need to support older Python versions.We will also demystify the Formatting Mini‑Language – the set of specifiers that control alignment, padding, numeric precision, and thousands separators.
Before f‑strings and .format(), Python used the % operator (inspired by C's printf). It works, but it is limited and less readable.
name = "Alice"
age = 30
print("Name: %s, Age: %d" % (name, age)) # Clunky and error‑prone with type mismatches
Why we don't use it in new code: it struggles with complex data types, has limited formatting options, and the syntax is inconsistent. We mention it only so you recognize it in legacy code.
.format() Method – The WorkhorseThe .format() method is called on a string that contains curly braces {} as placeholders. You pass the replacement values as arguments, and Python fills the braces in order (or by index/keyword).
name = "Bob"
score = 85
print("Student: {}, Score: {}".format(name, score)) # Student: Bob, Score: 85
The first {} takes the first argument (name), the second {} takes the second (score).
You can explicitly number the placeholders to reorder or reuse values.
print("{1} is {0} years old.".format(25, "Charlie")) # Charlie is 25 years old.
# {0} is 25, {1} is "Charlie"
print("{0} {0} {1}".format("hi", "bye")) # hi hi bye
You can pass named arguments to .format() for extra clarity.
print("User: {user}, ID: {uid}".format(user="Alice", uid=1024))
# Output: User: Alice, ID: 1024
A common pattern is to use ** to unpack a dictionary as keyword arguments.
info = {"name": "Eve", "job": "Engineer"}
print("{name} works as an {job}".format(**info))
# Output: Eve works as an Engineer
You can also index into list-like arguments directly inside the placeholders (without using **).
data = [10, 20, 30]
print("First: {0[0]}, Last: {0[2]}".format(data)) # First: 10, Last: 30
f‑strings (short for "formatted string literals") are the preferred way to format strings today. By prefixing a string with f or F, you can embed variables and expressions directly inside {}. They are evaluated at runtime.
name = "Diana"
city = "Paris"
print(f"{name} lives in {city}.") # Diana lives in Paris.
Any valid Python expression can go inside the braces—arithmetic, function calls, method chaining, etc.
x = 5
y = 10
print(f"{x} + {y} = {x + y}") # 5 + 10 = 15
words = ["Hello", "World"]
print(f"Joined: {', '.join(words)}") # Joined: Hello, World
print(f"Length of name: {len(name)}") # Length of name: 5
person = {"name": "Frank", "age": 42}
print(f"{person['name']} is {person['age']} years old.")
class User:
def __init__(self, name):
self.name = name
u = User("Grace")
print(f"User: {u.name}")
= Specifier (Python 3.8+)A fantastic shortcut for debugging: f"{var=}" expands to the variable name, an equals sign, and its value.
score = 95.678
print(f"{score=}") # score=95.678
print(f"{score=:.2f}") # score=95.68 (combines with formatting!)
This saves you from typing print(f"score = {score}").
You can use triple quotes to create multi‑line formatted strings.
name = "Isaac"
age = 35
message = f"""
Hello {name},
You are {age} years old.
Welcome aboard!
"""
print(message)
You can freely mix single and double quotes inside the string, as long as they don't conflict with the outer quotes.
print(f"She said: \"Hello {name}!\"") # Works fine
Important Note: f‑strings cannot contain backslashes inside the expression part (e.g.,
f"{1\n2}"is invalid). You can, however, use backslashes in the string part outside the braces.
Both f‑strings and .format() use the same mini‑language inside the {} braces. The general syntax is:
{value:[[fill]align][sign][#][0][width][grouping_option][.precision][type]}
We'll cover the most practical parts:
:< – left align (default for strings).:> – right align (default for numbers).:^ – center align.The width defines the minimum field size. You can also specify a fill character (default is space).
text = "Python"
print(f"{text:>10}") # ' Python' (right align in 10 spaces)
print(f"{text:<10}") # 'Python ' (left align)
print(f"{text:^10}") # ' Python ' (center)
print(f"{text:*^10}") # '**Python**' (fill with *)
With .format():
print("{:>10}".format(text)) # ' Python'
:.2f – fixed‑point notation with 2 decimal places.:.2% – percentage, multiplying by 100 and showing 2 decimal places.:.2e – scientific notation with 2 decimals.pi = 3.14159265
print(f"{pi:.3f}") # 3.142
print(f"{pi:.2%}") # 314.16% (3.1415 * 100)
print(f"{pi:.2e}") # 3.14e+00
# Using .format()
print("{:.3f}".format(pi)) # 3.142
Use a comma , or underscore _ to group digits.
num = 1234567890
print(f"{num:,}") # 1,234,567,890
print(f"{num:_}") # 1_234_567_890
You can chain specifiers. Order matters (usually: fill/align, width, grouping, precision, type).
value = 12345.6789
print(f"{value:>15,.2f}") # ' 12,345.68' (right align, width 15, commas, 2 decimals)
# With .format()
print("{:>15,.2f}".format(value))
You can use variables to control width or precision inside the braces.
width = 10
precision = 3
x = 1.23456
print(f"{x:^{width}.{precision}f}") # ' 1.235 ' (center in width 10 with 3 decimals)
# In .format(), you can nest:
print("{:^{}.{}f}".format(x, width, precision))
Use 0 instead of a fill character for numeric zero‑padding.
print(f"{7:05d}") # 00007
print(f"{7:0>5d}") # 00007 (equivalent)
Use + to always show a sign, - only for negative (default), and space for a space before positive numbers.
print(f"{5:+d}") # +5
print(f"{-5:+d}") # -5
print(f"{5: d}") # ' 5' (space)
This is a common real‑world use case. Combine datetime objects with the mini‑language (or use strftime).
from datetime import datetime
now = datetime.now()
print(f"{now:%Y-%m-%d %H:%M:%S}") # 2026-08-11 10:30:45 (example)
print("{:%B %d, %Y}".format(now)) # August 11, 2026
| Feature | f‑strings (Python 3.6+) | .format() |
|---|---|---|
| Readability | ⭐⭐⭐⭐⭐ (most readable) | ⭐⭐⭐⭐ |
| Performance | Fastest (evaluated at compile time) | Slightly slower |
| Dynamic Formatting | Can use variables inside specifier (e.g., {x:^{width}}) |
Yes, via nesting {:{}} |
| Logging | Caution: f‑strings are evaluated immediately, even if the log level is lower (can be wasteful). Use % or .format() with logging module to defer evaluation. |
Good for logging (evaluation deferred) |
| Backward Compatibility | Requires Python 3.6+ | Python 3.0+ (widely supported) |
General Rule: Use f‑strings for most day‑to‑day formatting. Use .format() when you need dynamic template strings (e.g., templates loaded from a file) or when maintaining a codebase that runs on Python < 3.6.
What is the output of f"{2 ** 5}"?
32"2 ** 5"2510Given s = "hello", what does f"{s.upper()}" produce?
"s.upper()""HELLO""Hello""hello"What does the expression f"{3.14159:.3f}" evaluate to?
'3.142''3.141''3.14''3.1416'True or False: .format() can take both positional and keyword arguments in the same call.
What is the output of "{1} {0}".format("world", "hello")?
"hello world""world hello""hello hello""world world"Which f‑string specifier would you use to right‑align a string in a field of width 10?
{s:<10}{s:>10}{s:^10}{s:=10}What does f"{1234567:,}" output?
'1,234,567''1234567''1.234.567''1 234 567'In Python 3.8+, what is the output of x = 5; f"{x=}"?
"5""x=5""{x=}""x = 5" (with spaces)How do you format the number 0.1234 as a percentage with two decimal places using an f‑string?
f"{0.1234:.2%}"f"{0.1234:%}"f"{0.1234:.2f}%"f"{0.1234:2%}"Which method is generally preferred for new Python code (Python 3.6+)?
% formatting.format()str.join()Exercise 1: Receipt Printer
Write a program that takes a product name (string), a quantity (int), and a unit price (float). Print a nicely formatted receipt line that:
Product Qty Total
Laptop 3 3,599.97
Exercise 2: Dynamic Data Table
Given a list of tuples: data = [("Alice", 85, 92), ("Bob", 78, 88), ("Charlie", 95, 90)] (name, midterm, final).
Write a script that prints a header and each row using f‑strings, with:
Exercise 3: User Profile Builder
Create a profile dictionary with keys first_name, last_name, age, height_m.
Write f‑strings that produce:
"First: John | Last: Doe | Age: 30""Height: 1.75m" (height with 2 decimals)"John Doe is 30 years old and 1.75 meters tall."Exercise 4: Date Formatter
Write a function format_date(year, month, day) that returns two formatted strings using both f‑strings and .format() (try both):
YYYY-MM-DD (e.g., 2026-08-11)Month DD, YYYY (e.g., August 11, 2026) – you'll need to map month numbers to names.Exercise 5: Convert Between Methods
Rewrite the following print() calls using the other method:
a) print("Hello, {}!".format(name)) → convert to f‑string.
b) print(f"{item}: ${price:.2f}") → convert to .format().
1. Grade Report Generator
Write a script that reads a text file containing student records in the format:
Name, Homework1, Homework2, Midterm, Final
(Simulate the file by using a multi‑line string or a list of strings).
Calculate the final grade as 0.2*HW1 + 0.2*HW2 + 0.3*Midterm + 0.3*Final.
Generate a report formatted as follows:
------------------------------------------------------------
Name HW1 HW2 Mid Final Final %
------------------------------------------------------------
Alice 95 80 85 90 87.50%
Bob 70 75 80 85 78.75%
...
Align all columns neatly. The Final % column should show a percentage with 2 decimals (e.g., 87.50%). Include a footer with the class average.
2. Dynamic Text Alignment Function
Write a function format_column(text, width, alignment='left', fill=' ') that uses f‑strings (or .format()) to return the text formatted inside a field of the given width. The alignment parameter can be 'left', 'right', or 'center'. Do not use manual string padding (like + or *); use the format specifiers dynamically.
3. Invoice Generator
Create an invoice formatter. Given the following data:
invoice = {
"number": "INV-101",
"date": "2026-08-11",
"customer": "Acme Corp",
"items": [
{"desc": "Widget", "qty": 4, "price": 9.99},
{"desc": "Gadget", "qty": 2, "price": 19.95},
{"desc": "Doodad", "qty": 1, "price": 45.00}
],
"tax_rate": 0.08
}
Write a program that prints a structured invoice:
=== INVOICE #INV-101 (2026-08-11) ===
Customer: Acme Corp
-----------------------------------
Description Qty Unit Price Total
Widget 4 9.99 39.96
Gadget 2 19.95 39.90
Doodad 1 45.00 45.00
-----------------------------------
Subtotal: 124.86
Tax (8.00%): 9.99
TOTAL: 134.85
Align the amounts to the right, with 2 decimal places and commas for thousands (if applicable). The tax rate should show as 8.00%.
4. Logger Formatter with .format()
Write a logging utility that uses a template string (stored in a variable) and the .format() method. The template should support placeholders for timestamp, level, module, and message.
Create a list of log entries (as dictionaries). Iterate through them and print formatted log lines. Demonstrate how .format() allows you to load templates dynamically (e.g., from a config file) – something f‑strings cannot easily do.
5. Nested Formatting Challenge
You have a list of floating‑point numbers: [0.001, 0.12345, 123.456, 1e-5].
Using a single f‑string inside a loop, print each number in three formats side‑by‑side in a table:
split(',') on each line, calculate grade. Use : alignment specifiers for columns. For the footer, calculate average and print with - separators.format_column, create a variable align mapped to <, >, or ^. Use f"{text:{fill}{align}{width}}".invoice["items"], calculate subtotal. Use f"{amount:>10,.2f}" for totals. Compute tax and total.template = "{timestamp} [{level}] {module}: {message}". Use template.format(**entry).You are now equipped with two powerful string formatting tools:
.format() for dynamic templates and backward compatibility.You also understand the mini‑language specifiers that control alignment, padding, numeric precision, grouping, and signs. With these skills, you can produce professional, clean, and dynamic textual outputs for any application—from console scripts to full‑blown report generators.
Next Steps: In Tutorial 4, we will explore Lists in Depth, covering list comprehensions, copying, and advanced iteration techniques.
Happy formatting!