Python with AI Tutorial · Chapter 10 of 48
Python string formatting is how you insert values into text and control how they look: two decimal places, thousands separators, percentages, padding and alignment. Modern code uses f-strings, written with an f before the opening quote, while the older format() method is still common in templates and existing projects. This chapter covers both with business-style examples.
f-string basics
Put an f in front of the string and write any variable or expression inside curly braces. Python evaluates it and converts the result to text automatically, so there is no need for str() or the + operator.
product = "Wireless Mouse"
price = 24.5
qty = 3
print(f"{product} x {qty} = {price * qty}")
Output: Wireless Mouse x 3 = 73.5
The expression price * qty is calculated right inside the braces. f-strings were added in Python 3.6 and are the fastest of the three formatting styles, so they are the default choice for new code.
Why does this matter so much in practice? Almost every script you write for work ends in text that a person reads: a console summary, a log line, an email body, a label on a chart. Getting the formatting right at the point where the value becomes text saves you from cleaning it up later in Excel or by hand, and it makes small mistakes such as a missing decimal place far easier to spot.
Format specifiers for numbers
After the value, add a colon and a format specification. This is where you control decimals, separators and percentages, which matters when the output is a report someone else will read.
revenue = 1234567.891
growth = 0.083
print(f"Revenue: {revenue:,.2f}")
print(f"Growth: {growth:.1%}")
print(f"Units: {4200:,}")
print(f"Rounded: {revenue:.0f}")
Output: Revenue: 1,234,567.89 Growth: 8.3% Units: 4,200 Rounded: 1234568
The % specifier multiplies by 100 and adds the sign for you, so you store the raw ratio and let formatting handle the presentation.
| Specifier | Meaning | Example | Result |
|---|---|---|---|
.2f |
Fixed point, 2 decimals | f"{3.14159:.2f}" |
3.14 |
, |
Thousands separator | f"{1500000:,}" |
1,500,000 |
.1% |
Percentage, 1 decimal | f"{0.256:.1%}" |
25.6% |
d |
Integer | f"{42:d}" |
42 |
05d |
Zero-padded to width 5 | f"{42:05d}" |
00042 |
e |
Scientific notation | f"{1500000:.2e}" |
1.50e+06 |
<10 |
Left align in 10 spaces | f"{'Q3':<10}|" |
Q3 | |
>10 |
Right align in 10 spaces | f"{'Q3':>10}|" |
Q3| |
^10 |
Centre in 10 spaces | f"{'Q3':^10}|" |
Q3 | |
+ |
Always show the sign | f"{12:+d}" |
+12 |
Padding and alignment
A width after the colon reserves space, and <, > or ^ sets the alignment inside it. This is the quickest way to print a tidy table in the console without any extra library.
items = [("Laptop", 899.0), ("Monitor", 249.99), ("Keyboard", 45.5)]
print(f"{'Item':<10}{'Price':>10}")
for name, price in items:
print(f"{name:<10}{price:>10.2f}")
Output: Item Price Laptop 899.00 Monitor 249.99 Keyboard 45.50
Text normally aligns left and numbers align right, so < for the name column and > for the price column gives the layout people expect from a spreadsheet.
Pick the width from the longest value you expect plus a little breathing room. If a value is wider than the width you asked for, Python does not truncate it; it simply spills over and pushes the rest of the line along, so a generous width is safer than a tight one.
Ask the assistant to build a console report with aligned columns and a total line, then to explain every format specifier it used.
Using Python 3.12 f-strings only, print a sales table for these rows: ("North", 125000.5), ("South", 98250), ("East", 143999.99). Columns: Region left-aligned in 8 characters, Sales right-aligned in 14 characters with thousands separators and 2 decimals. Add a header row, a dashed line and a TOTAL row. After the code, explain each format specifier you used in one sentence each.
Expressions and method calls inside braces
Anything that produces a value can sit inside the braces: arithmetic, function calls, method calls and indexing. Keep the expressions short so the string stays readable.
name = "anita desai"
scores = [88, 92, 79]
print(f"{name.title()} averaged {sum(scores) / len(scores):.1f}")
print(f"{len(scores)} tests, best {max(scores)}")
Output: Anita Desai averaged 86.3 3 tests, best 92
The = debug specifier
Python 3.8 added = after an expression to print both the expression and its value. It saves typing when you are checking a calculation, and it combines with the normal format spec.
units = 120
unit_price = 8.75
print(f"{units=}, {unit_price=}")
total = units * unit_price
print(f"{total=:.2f}")
Output: units=120, unit_price=8.75 total=1050.00
Zero padding and dates
Order numbers and IDs often need leading zeros, and dates accept the same strftime codes you will meet in the dates chapter directly inside the braces.
from datetime import date
order_no = 42
d = date(2026, 9, 5)
print(f"ORD-{order_no:05d}")
print(f"{d:%d %b %Y}")
print(f"{d:%Y-%m-%d}")
Output: ORD-00042 05 Sep 2026 2026-09-05
Nested widths and multi-line output
The width itself can be a variable placed in its own pair of braces, which lets one setting control the whole layout. For longer messages, put several f-strings inside parentheses on separate lines; Python joins adjacent string literals into one, so you get a readable multi-line template without a single +.
width = 12
label = "Revenue"
value = 98765.4
print(f"{label:>{width}}: {value:>{width},.2f}")
report = (
f"Region: North\n"
f"Orders: {215}\n"
f"Average: {value / 215:.2f}"
)
print(report)
Output:
Revenue: 98,765.40
Region: North
Orders: 215
Average: 459.37
Changing width once now re-aligns every column that uses it, which is exactly the kind of small refactor that keeps report scripts maintainable as they grow.
The format() method
Before f-strings, str.format() did the same job. Empty braces are filled in order, numbered braces can repeat a value, and named braces make long templates easier to read. It is still useful when the template is stored separately from the data, for example in a settings file or a database.
template = "Dear {}, your invoice {} of {:.2f} is due on {}."
print(template.format("Meera", "INV-104", 1200, "15 Oct"))
print("{name} works in {dept}".format(name="Arjun", dept="Sales"))
print("{0} {1} {0}".format("ping", "pong"))
Output: Dear Meera, your invoice INV-104 of 1200.00 is due on 15 Oct. Arjun works in Sales ping pong ping
Every format specifier from the table works identically in format(), so "{:,.2f}".format(revenue) and f"{revenue:,.2f}" produce the same text.
Old-style % formatting
You will still see % formatting in older tutorials and logging calls. It works, but it is easy to get the argument order wrong, so prefer f-strings in your own code.
print("%s sold %d units at %.2f each" % ("Rahul", 15, 19.5))
Output: Rahul sold 15 units at 19.50 each
f"{{total}} = {total}" prints {total} = 1050.0. Also remember that the quotes inside the braces must differ from the outer quotes in Python versions before 3.12, so f"{data['key']}" is the safe pattern everywhere.Paste some formatting code that produces ugly output and ask the assistant to fix it and explain the difference between f-strings, format() and %.
This Python code prints a messy report. Rewrite it with f-strings so every amount shows a currency symbol, thousands separators and two decimals, and dates print as "05 Sep 2026". Then explain in plain English when I should still use str.format() or % formatting instead of f-strings.
total = 15234.5
from datetime import date
d = date(2026, 9, 5)
print("Total is " + str(total) + " on " + str(d))
Common mistakes
Most formatting bugs come from one of these five slips, and each has a very short fix.
- Forgetting the
fprefix, so the braces are printed literally as{price}. - Putting the format spec on the wrong side of the colon. It is
{value:.2f}, never{.2f:value}. - Using
:.2fon a string. Fixed-point formatting only works on numbers and raisesValueErrorotherwise. - Reusing the outer quote type inside the braces on Python 3.11 or earlier, which is a
SyntaxErrorthere. - Mixing numbered and empty braces in one
format()call, such as"{0} {}", which is not allowed.
Exercise
You have employee = "ravi kapoor", salary = 84500 and bonus_rate = 0.125. Print one line in the form Ravi Kapoor earns 84,500 with a 12.5% bonus of 10,562.50, using a single f-string.
Show answer
employee = "ravi kapoor"
salary = 84500
bonus_rate = 0.125
print(f"{employee.title()} earns {salary:,} with a {bonus_rate:.1%} bonus of {salary * bonus_rate:,.2f}")
Output: Ravi Kapoor earns 84,500 with a 12.5% bonus of 10,562.50
Four different specifiers in one line: title() for the name, , for the integer, .1% for the rate and ,.2f for the calculated bonus.
Related chapters
- Python Strings – slicing and the core string methods.
- Python Numbers – int, float and rounding behaviour.
- Python Dates – all the strftime codes you can use inside braces.
- Python with AI course hub – all 48 chapters in order.
FAQ
Should I use f-strings or format() in Python?
Use f-strings for new code. They are shorter, faster and easier to read. Reach for format() only when the template text is stored separately from the values, such as a message template loaded from a file.
How do I format a number with commas and two decimal places?
Use the specifier ,.2f, for example f”{1234567.891:,.2f}” gives 1,234,567.89. The comma adds thousands separators and .2f fixes two decimal places.
How do I print curly braces inside an f-string?
Double them. Writing f”{{ and }}” prints { and }, while single braces are treated as placeholders for expressions.
Working with spreadsheets too? Ready-made Excel, Google Sheets and Power BI templates are at NextGenTemplates.com.
Chapter 10 of 48 · Python with AI: all 48 chapters



