Post

f-strings: formatting text in Python

How f-strings work in Python: putting expressions in braces, format specs for width, alignment, precision, thousands separators and percentages, the = debug specifier, and !r conversions.

An f-string is a string with an f before the opening quote. Inside it, anything in braces is an expression that Python evaluates and puts into the text. It’s the standard way to build output in Python today, and it replaces the older % formatting and str.format() in most new code.

Every example here was run with Python 3.13. The syntax was checked against the official f-strings reference and the format specification mini-language.

The basics

name = "Ada"
age = 36
print(f"{name} is {age} years old")
print(f"{age + 1} next year")
print(f"{name.upper()}")
Ada is 36 years old
37 next year
ADA

The docs say “expressions in formatted string literals are treated like regular Python expressions”.

Use quotes of the other kind inside the braces if the expression needs quotes. Since Python 3.12 you can reuse the same kind, but it’s clearer not to:

print(f"{'nested ' + 'quotes'}")
nested quotes

Literal braces are written doubled:

print(f"{{literal braces}}")
{literal braces}

Format specs: after the colon

A colon after the expression starts a format spec. The general shape is fill, align, sign, width, grouping, precision and type. You don’t need all of them.

Decimals and thousands

pi = 3.14159265
price = 1234567.891
print(f"{pi:.2f}")
print(f"{price:,.2f}")
3.14
1,234,567.89

.2f means two digits after the decimal point. The comma inserts a separator every three digits. Use _ instead of , if you want underscores.

print(f"{1_000_000:_}")
1_000_000

Padding and alignment

Use < for left, > for right, and ^ for centered, with a width:

name = "Ada"
print(f"{name:>6}|{name:<6}|{name:^7}|")
   Ada|Ada   |  Ada  |

A character before the alignment sign is the fill. * in front of ^ gives stars:

print(f"{'x':*^9}")
****x****

Zero-padding a number: 05d gives five digits, filled with zeros.

age = 36
print(f"{age:05d}")
00036

Percentages, hex and binary

print(f"{0.257:.1%}")
print(f"{255:x} {255:b} {255:#o}")
25.7%
ff 11111111 0o377

% multiplies by 100 and adds a percent sign. x, b and o give hex, binary and octal. The # adds the prefix.

Dynamic width

The width or precision can itself come from a variable, written in a nested pair of braces:

pi = 3.14159265
width = 8
print(f"{pi:{width}.3f}|")
   3.142|

The = debug specifier

Put = after an expression to print the expression text and its value. Python 3.8 added this:

age = 36
print(f"{age=}")
age=36

Conversions: !r

!r shows the value the way repr() does, which is useful for strings with quotes or spaces. The docs list !s (str()) and !a (ascii()) too:

name = "Ada"
print(f"{name!r}")
'Ada'

Common mistakes

  • Forgetting the f. "{name} is {age}" prints the braces as they are.
  • Putting a format spec where a conversion goes. The conversion is !r, and the spec is : followed by the format.
  • Relying on rounding in the display. f"{2.675:.2f}" shows 2.67, for the same reason as round(2.675, 2) in the number operations post.
  • Formatting user input directly. Only use f-strings with trusted text in the template. Don’t build the template from user input.

What’s next

Next: number operations: /, //, % and **.