Python has several ways to build strings with dynamic values. The modern, recommended approach is the f-string (formatted string literal), introduced in Python 3.6.
f-strings — the modern default
name =
age =
msg =
The f prefix lets you embed expressions directly in {} — concise, readable, and fast. This is the preferred method in modern Python.
f"{3.14159:.2f}" # "3.14" — 2 decimal places
f"{1000000:,}" # "1,000,000" — thousands separator
f"{0.85:.1%}" # "85.0%" — percentage
f"{42:05d}" # "00042" — pad with zeros to width 5
f"{'hi':>10}" # " hi" — right-align in width 10
f"{'hi':^10}" # " hi " — center
The :spec after the value controls precision, padding, alignment, and number formatting — powerful for clean output.
f"{name=}" # "name='Ann'" — prints both the expression AND value
The = is handy for quick debugging — it shows the variable name and its value.
"{} is {}".format(name, age) # str.format() — pre-f-string standard
"%s is %d" % (name, age) # %-formatting — old C-style (avoid in new code)
name + " is " + str(age) # concatenation — verbose, error-prone
.format() is still common in older code; %-formatting is legacy; raw concatenation is discouraged (verbose and requires manual str() conversion).
String formatting is needed constantly — building messages, logs, output, queries.
F-strings are the modern, readable, and efficient standard, and knowing their format specifiers (precision, padding, alignment, separators) lets you produce clean, professional output without verbose code.
Recognizing the older .format() and % styles helps when reading existing codebases.
Using f-strings well is a small but pervasive part of writing clear, idiomatic Python.