Python f-Strings: Formatting Done Right

A practical tour of what goes after the colon in a Python f-string: alignment and width, thousands separators, decimal places and percentages, signs and padding, dates and times, repr and the = debugging specifier, nested specs for dynamic widths, and building aligned text tables.

GloryTecks Team

Training & Placement Faculty

6 May 2026 6 min read

Last updated: 24 September 2026

Share:

Most people use f-strings for one thing: dropping a variable into text, as in f"Hello, {name}". The part after the colon inside the braces is where f-strings become genuinely useful. It is a small formatting language that controls width, alignment, separators, decimal places, percentages and dates, and it is the same language format() and str.format() use (Python docs: format specification mini-language). Learn it once and you stop writing round() and string padding by hand.

Anatomy of a replacement field

A replacement field is {expression!conversion:format_spec}. Only the expression is required. The conversion (!r, !s or !a) chooses repr(), str() or ascii() before formatting, and the format spec after the colon controls the layout (Python reference: f-strings). The expression can be any Python expression: an arithmetic result, a method call, an indexed value.

Numbers: separators, decimals, percentages

python
revenue = 12345678.456
growth = 0.0734
units = 42

print(f"{revenue:,.2f}")        # thousands separator, 2 decimals
print(f"{revenue:_.0f}")        # underscore separator, no decimals
print(f"{growth:.1%}")          # multiply by 100 and add %
print(f"{units:05d}")           # zero-pad to width 5
print(f"{revenue:.3e}")         # scientific notation
print(f"{-7.5:+.1f} {7.5:+.1f}")  # always show the sign
text
12,345,678.46
12_345_678
7.3%
00042
1.235e+07
-7.5 +7.5

.2f rounds for display only; the variable keeps its full value, which is exactly what you want in a report. The % type multiplies by 100 for you, so store rates as fractions (0.0734) and format them as percentages at the edge.

A caution for Indian number formatting: the , option always groups in thousands (12,345,678), not in lakhs and crores (1,23,45,678). For lakh grouping, use a locale-aware library or a small helper function; the format language has no option for it.

Width and alignment

python
for name, amount in [("Notebook", 120), ("Stapler", 1450.5), ("Pen", 25)]:
    print(f"|{name:<10}|{amount:>10,.2f}|{name:^10}|")
print(f"{'total':*^20}")
text
|Notebook  |    120.00| Notebook |
|Stapler   |  1,450.50| Stapler  |
|Pen       |     25.00|   Pen    |
*******total********

<, > and ^ align left, right and centre within the width; a character before the alignment symbol, like the * in the last line, fills the space. Right-aligning numbers with a fixed number of decimals lines up the decimal points, which is what makes a column of figures readable. Text longer than the width is not truncated; add a precision to a string ({name:.8}) to cut it.

Dates and times

date and datetime objects accept strftime codes directly after the colon (Python docs: format codes):

python
from datetime import datetime

paid_at = datetime(2026, 9, 25, 14, 5)
print(f"{paid_at:%d %b %Y, %H:%M}")
print(f"{paid_at:%Y-%m-%d}  week {paid_at:%V}  {paid_at:%A}")
text
25 Sep 2026, 14:05
2026-09-25  week 39  Friday

For machine-readable output, such as logs, files and APIs, prefer paid_at.isoformat(), which includes the offset for time-zone-aware values.

repr and the = specifier for debugging

python
city = "Hyderabad "
print(f"{city}|")          # the trailing space is invisible
print(f"{city!r}|")        # repr shows it
print(f"{city=}")          # expression text, then its repr
print(f"{units * 3 = }")   # spaces around = are kept
text
Hyderabad |
'Hyderabad '|
city='Hyderabad '
units * 3 = 126

!r uses repr(), which shows quotes and invisible characters: invaluable when a value "looks right" but does not match. The = specifier (Python 3.8+) prints the expression itself followed by its value, so a debugging line no longer needs the variable name typed twice. By default it uses repr() too.

Dynamic widths with nested fields

The format spec can itself contain replacement fields, so widths and precision can come from variables:

python
rows = [("Hyderabad", 1523.456), ("Visakhapatnam", 98.1)]
width = max(len(city) for city, _ in rows) + 2
decimals = 1
for city, value in rows:
    print(f"{city:<{width}}{value:>10.{decimals}f}")
text
Hyderabad          1523.5
Visakhapatnam        98.1

Computing the column width from the data means the table fits whatever names appear, with no magic numbers.

Literal braces and quotes

Double a brace to print it literally: f"{{placeholder}}" prints {placeholder}. Before Python 3.12, the expression inside an f-string could not reuse the string's own quote character or contain a backslash; from 3.12 those restrictions are lifted (PEP 701). If your code must run on older versions, use the other quote type inside, as in f"{row['name']}".

Choosing precision honestly

Formatting decides what readers believe about your numbers, so a few habits matter more than any particular spec:

  • Show only the precision the data supports. A survey average printed as 3.4172 suggests an accuracy the data does not have; one decimal place is more honest.
  • Keep a column consistent. Every value in a column should have the same number of decimals, so that 120.00 and 1,450.50 line up and compare at a glance.
  • Round for display, not for storage. Keep full precision in variables and files, and round only in the f-string at the moment of output. Rounding early and then summing produces totals that disagree with the detail rows.
  • Expect rounded percentages not to add up to 100. Three shares of 33.3% sum to 99.9%. Either say so in a note, or adjust the largest share so the displayed values reach 100.

When not to use an f-string

  • Logging calls. logger.info("loaded %s rows", n) defers formatting until the message is actually emitted, and log tools can group messages by their template. An f-string formats every time, even when the level is switched off.
  • SQL queries. Never build SQL by formatting values into the string; that is how injection happens. Pass values as query parameters.
  • User-facing templates that translators or non-developers edit. Use a template system, where the text does not contain code.

Quick reference

GoalSpecExample output
Two decimals, grouped:,.2f12,345,678.46
Percentage, one decimal:.1%7.3%
Zero-pad integer to 5:05d00042
Right-align in 10:>10right-aligned
Centre with fill:*^20centred, starred
Scientific:.3e1.235e+07
Hex, binary:x, :b2a, 101010
Date:%d %b %Y25 Sep 2026
Show repr!rquotes visible
Debug=expression and value

Are f-strings faster than format() or %-formatting?

Usually a little, because the expression is compiled into the string-building code directly. The difference rarely matters; readability is the reason to prefer them.

How do I format currency with the rupee symbol?

Put the symbol in the literal text, as in f"₹{amount:,.2f}". The format spec controls digits, grouping and decimals, not currency symbols; remember that the grouping is in thousands, not lakhs.

Can I reuse a format spec in several places?

Yes. Store it in a variable and nest it, as in f"{value:{money}}" with money = ",.2f", or use format(value, money) outside f-strings.

Should I still use str.format()?

When the template is stored separately from the values, yes: a string such as "Hello {name}" kept in a settings file can be filled later with template.format(name=...). f-strings are evaluated where they are written, so they cannot be loaded from a file.

Why does round() give different results from the f-string?

Both use the float's binary value, so a number like 2.675 is actually slightly below 2.675 and rounds down in both. For exact decimal rounding of money, use the decimal module.

Formatting is one of many string operations; Python string methods every developer should know covers splitting, cleaning and searching text. The Python best practices article shows f-strings among other everyday idioms. String operations, methods and string formatting are part of the strings and regular expressions module of the Python Programming course.

Sources

GloryTecks Team

Training & Placement Faculty

The GloryTecks faculty team mentors learners across Data Science, AI and Data Engineering, with a focus on real-time projects and placement readiness in Hyderabad.

Want to master Python?

Join GloryTecks’ expert-led Python Programming training in Hyderabad with real-time projects and 100% placement support.

Explore course