F-Strings

Concepts covered: f-strings

An f-string is a string literal with an f before the opening quote. Each pair of braces inside it holds a Python expression; when the line runs, Python evaluates the expression, converts the result to text, and puts that text in place of the braces.

table = "orders"
rows = 1500
print(f"Loaded {rows} rows into {table}")
print(f"{table.upper()} has {rows // 100} hundred rows")
print("Loaded " + str(rows) + " rows into " + table)

Output:

Loaded 1500 rows into orders
ORDERS has 15 hundred rows
Loaded 1500 rows into orders

The concatenated version needs str(rows), because + between a string and an int raises TypeError. The f-string converts for you, and the sentence reads in one piece with each value where it appears. {rows // 100} and {table.upper()} put arithmetic and method calls inside the braces, which accept any expression.

Indexes, slices and searches inside the braces

uri = "spotify:track:6rqhFgbbKwnb9MLmUQDhG6"
track_id = uri[uri.rfind(":") + 1:]
print(f"https://open.spotify.com/track/{track_id}")
print(f"id length: {len(track_id)}")
print(f"kind: {uri[8:13]}, first: {track_id[0]}")

Output:

https://open.spotify.com/track/6rqhFgbbKwnb9MLmUQDhG6
id length: 22
kind: track, first: 6

Everything from the earlier sections works inside braces. The ID comes from slicing after the last colon, and the share link is assembled around it. len and an index also run in place. A long expression is clearer as a variable of its own, as track_id is here, which keeps the template readable.

Formatting numbers after a colon

revenue = 1299.5
share = 0.125
events = 2500000
month = 3
print(f"{revenue:.2f}")
print(f"{share:.1%}")
print(f"{events:,}")
print(f"2024-{month:02}-15")

Output:

1299.50
12.5%
2,500,000
2024-03-15

A colon after the expression starts a format spec. .2f prints a float with exactly 2 decimals. .1% multiplies by 100 and adds a percent sign, and , groups thousands. For 02, Python pads with zeros to a width of 2. Zero padding matters in dates and partition paths: as text, month=10 sorts before month=3, while month=03 sorts before month=10.

Mixing slices and specs in one line

track_id = "6rqhFgbbKwnb9MLmUQDhG6"
plays = 1820
total = 14560
print(f"{track_id[:6]}: {plays:,} plays, {plays / total:.1%} of the day")

Output:

6rqhFg: 1,820 plays, 12.5% of the day

Each spec applies only to the value in its own braces, so one f-string can hold a slice with no spec, a count grouped with ,, and a ratio shown with .1%. The division happens inside the braces and gives 0.125; the spec decides how that float looks and leaves the computed value alone.

Braces, quotes and the = shortcut

key = "region"
value = "eu-west"
print(f'{{"{key}": "{value}"}}')
print(f"{key=}")
print(f"{len(value)=}")

Output:

{"region": "eu-west"}
key='region'
len(value)=7

Doubled braces {{ and }} print 1 literal brace each, which JSON-shaped text needs. Writing = after an expression prints the expression and its value joined by an equals sign, with quotes around strings: key='region'. Before Python 3.12, including the 3.9 these examples run on, quotes inside the braces must differ from the ones around the f-string: f"{name.replace('-', '_')}" works, and reusing double quotes inside is a SyntaxError. From 3.12 on, reusing the same quotes is allowed.

When the values are read

status = "running"
msg = f"job is {status}"
status = "done"
print(msg)
print(f"job is {status}")

Output:

job is running
job is done

An f-string is evaluated once, on the line where it appears. msg holds the finished text job is running, and changing status afterwards can't reach back into it. The second print evaluates a fresh f-string, so it sees done.

for hour in range(3):
    print(f"hour={hour:02}")

Output:

hour=00
hour=01
hour=02

Inside a loop, the f-string sits on a line that runs once per pass, so each pass evaluates it with that pass's hour. A job that writes one partition per hour runs the same template 24 times and gets 24 different paths, from hour=00 to hour=23.

What does this program print?

part = 4
total = 12
print(f"{{part}} {part}/{total} {part / total:.1%}")

About this lesson

This section is part of the Strings: Intermediate lesson on DataDriven, the home of data engineering. Lessons cover SQL, Python, Spark, data modeling and pipeline architecture, and each section pairs its explanation with worked examples and practice you run as you go.