Printing a raw variable is easy, but real programs usually need to control exactly how a value looks — how many decimal places a price has, whether a number is padded with zeros, or how several values are woven into a single sentence. Python gives you three ways to do this: the old % operator, the .format() method, and modern f-strings. All three solve the same problem, but they are not equally good choices in 2024-era Python.
If you are writing new code today, f-strings should be your default. They are the most readable, the fastest at runtime, and the easiest to debug because the expression you're printing sits right inside the string itself. The other two methods are covered here mainly so you can read and maintain older Python variables and codebases that predate Python 3.6.
f-strings: The Modern Standard
An f-string is a normal string literal prefixed with f. Anything inside curly braces {} is evaluated as a Python expression and inserted into the string.
name = "Asha"
age = 29
price = 19.999
# Basic interpolation
print(f"{name} is {age} years old")
# You can even call functions or do math inline
print(f"Next year, {name} will be {age + 1}")
# Format specifiers go after a colon
print(f"Price: ${price:.2f}") # 2 decimal places
print(f"Padded: {age:05d}") # pad to 5 digits with zeros
print(f"Aligned: |{name:<10}|{name:>10}|") # left/right align in a 10-char field
Output:
Asha is 29 years old
Next year, Asha will be 30
Price: $20.00
Padded: 00029
Aligned: |Asha | Asha|
The part after the colon is called a format spec. .2f means "fixed-point with 2 decimals," 05d means "integer padded to width 5 with leading zeros," and <10/>10 mean "left/right-align in a field 10 characters wide." These same format specs also work with .format().
The .format() Method
Before f-strings existed (Python 3.6, released 2016), .format() was the recommended approach. It still appears constantly in older code, libraries, and Stack Overflow answers, so it's worth recognizing.
name = "Asha"
age = 29
# Positional placeholders
print("{} is {} years old".format(name, age))
# Indexed placeholders (reusable, order-independent)
print("{0} is {0}... I mean, {0} is {1}".format(name, age))
# Named placeholders (more readable for many values)
print("{n} is {a} years old".format(n=name, a=age))
Output:
Asha is 29 years old
Asha is Asha... I mean, Asha is 29
Asha is 29 years old
The % Operator (Old Style)
This is the oldest style, borrowed from C's printf. You'll mostly see it in legacy codebases and some logging libraries. It uses %s for strings, %d for integers, and %f for floats.
name = "Asha"
age = 29
print("%s is %d years old" % (name, age))
Output:
Asha is 29 years old
Which One Should You Use?
For any new code, use f-strings — they're clearer to read, slightly faster, and let you embed arbitrary expressions (not just variable names) directly in the string. Reach for .format() only when you're maintaining code that already uses it, or in the rare case where the format string itself needs to be built dynamically and reused (e.g. loaded from a config file) before the values are known. The % operator is largely historical at this point — recognize it, but don't write new code with it. Once you're comfortable with formatting, a natural next step is combining it with a for loop to format many values at once, such as printing a neatly aligned table of numbers.
