Topic 1.9
Printing and f-strings
In one line
print shows values on the screen and can be tuned with sep and end. An f-string, written f"...{value}...", builds text by putting values into {} slots, and a format spec such as :.2f or :>8 controls how each value looks.
Think of it like this
A fill-in-the-blanks sentence on a worksheet. It says "Dear ____, your total is ____ rupees". Before it is handed out, the teacher writes the real name and amount into the blanks. An f-string is that template: the blanks are {curly brackets}, and Python fills them with the current values of your variables when the line runs.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- A function that shows values on the screen.
- sep
- The option of
printthat sets the text placed between values. - end
- The option of
printthat sets the text placed after the last value. - f-string
- A string starting with
fwhere{}slots are replaced by values. - Format spec
- The part after
:in a slot, which controls how the value is shown. - Placeholder
- A
{}slot inside an f-string. - str
- The readable text form of a value, for people.
- repr
- The exact text form of a value, for programmers, showing quotes and escapes.
Step by step
01print with several values
print accepts any number of values, converts each to text, joins them with a space and adds a new line. That is why print("Total:", 42) needs no conversion of the 42.
sep="" removes the spaces; sep="\t" puts a tab between columns. end="..." replaces the new line, so the next print continues on the same line.
print("a", "b", "c") # a b c
print("a", "b", "c", sep="-") # a-b-c
print("loading", end="...")
print("done") # loading...done
print() # a blank line02Your first f-string
Without an f-string you write "Name: " + name, and it breaks when the value is a number. With an f-string the same line is f"Name: {name}", and it works for any type, because each value is turned into text for you.
The text outside the braces is copied as is. The expression inside is evaluated, converted to text and put in its place.
name = "Asha"
qty = 3
print(f"Name: {name}")
print(f"Items: {qty}, double is {qty * 2}")03Format specs for numbers
Money and measurements need control. {score:.2f} rounds to two decimals and keeps the trailing zero (91.46, 5.00). {big:,} adds commas. {0.256:.1%} multiplies by 100 and adds a percent sign.
A zero before the width pads with zeros: {7:03d} gives 007. b and x show a whole number in binary or hex. The d in 03d stands for a whole number (a decimal integer).
score = 91.456
big = 1234567
print(f"Score: {score:.2f}") # Score: 91.46
print(f"{big:,}") # 1,234,567
print(f"{0.256:.1%}") # 25.6%
print(f"{7:03d} {255:b} {255:x}") # 007 11111111 ff04Format specs for alignment
A number after <, > or ^ is the width of the field. Python pads with spaces to reach that width. Tables of aligned columns are made this way. Text is left-aligned by default and numbers are right-aligned.
name = "Asha"
print(f"|{name:>8}|{name:<8}|{name:^8}|")
# | Asha|Asha | Asha |05The = debugging specifier
When you are tracking down a bug, print(f"{total=}") is faster than print("total:", total). It writes the expression exactly as you typed it, then =, then its value. A space around the = is kept.
x = 7
print(f"{x=}") # x=7
print(f"{x * 2 = }") # x * 2 = 1406str versus repr
print("hi\nthere") shows two lines, because print shows the *meaning*. print(repr("hi\nthere")) shows 'hi\nthere' on one line with quotes, so you can see the exact characters. Use repr (or !r) in debug messages: it reveals invisible spaces and tells "7" apart from 7.
text = "hi\nthere"
print(text) # two lines
print(repr(text)) # 'hi\nthere'
print(repr("7"), repr(7)) # '7' 7Try it yourself
- 1
Make a receipt line
Set
item = "Pen"andprice = 12.5. PrintPen 12.50using one f-string with:<10for the item and:.2ffor the price. - 2
Debug with =
Set
a = 4andb = 9. Printf"{a=} {b=} {a * b=}"and look at what you get. - 3
See the invisible
Set
word = "cat "(with a space at the end). Printwordand thenrepr(word). Which one shows the space?
Code & diagrams
Change the widths and the number of decimals and watch the output change.
Expected output
a b c
a-b-c
loading...done
xy
Name Score
1 2.5 True None
Name: Asha
Score: 91.46
1,234,567
| Asha|Asha | Asha |
25.6%
007 11111111 ff 3.142
{braces} and AshaThe = specifier needs Python 3.8 or later. Compare the two ways of showing text with a new line in it.
Expected output
x=7
x * 2 = 14
hi
there
'hi\nthere'
'hi\nthere'
"it's" 'say "hi"'
3.0 3.0
None None
False '7' 7Before Python 3.12 the first line was a SyntaxError. The second form works on every version.
Expected output
abcd
singleA for loop (Phase 2) repeats the indented lines once per item. Notice the widths, the commas, the percent, and how a spec can be nested with {width} and {precision}.
Expected output
Item Qty Price Total
---------------------------------------
Notebook 3 45.50 136.50
Pen 12 9.99 119.88
Geometry box 1 120.00 120.00
---------------------------------------
Grand total 376.38
Share of box 31.9%
[ 3.142]
2.67 0 2 1_234_567.9 -5 0xffBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
A misspelled name in a slot
Use a variable name that does not exist inside an f-string.
name = "Asha"
print(f"Hello, {nmae}")Break #2
A format code that does not match the type
Ask for the d (whole number) format on a decimal number.
score = 91.456
print(f"{score:.2d}")Myth vs fact
Myth
print returns the text it shows.
Fact
print shows text and returns None. If you want a string to keep, build it with an f-string or str().
Myth
An f-string is a different type from a normal string.
Fact
The result is an ordinary str. The f only tells Python to fill the braces when it builds the string.
Myth
round(x, 2) and f"{x:.2f}" do the same job.
Fact
round returns a number (5.0 stays 5.0); the format spec returns text with exactly two decimals (5.00). Use round for calculation and the format spec for display.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
An f-string is compiled to bytecode that evaluates each expression, calls
format()on it with the spec and joins the pieces, so it is usually faster than%or.format(), which parse a template at run time. Topic 13.3 shows the bytecode. The spec is handed to the value's__format__method, which is why dates take their own codes (f"{when:%Y-%m-%d}") and your own classes can define them. Specs can be nested:f"{x:{width}.{precision}f}". - ▸
Format specs round the exact stored value, not the decimal you typed.
f"{2.675:.2f}"is2.67(the float is slightly below 2.675), and ties go to even:f"{0.5:.0f}"is0andf"{2.5:.0f}"is2. Other specs worth knowing:_as a separator ({n:_}),+to always show the sign ({x:+.1f}),#xfor0xff,eandgfor scientific and general notation, and!s,!r,!aconversions.{x=}usesrepr, sof"{name=}"showsname='Asha'with quotes. - ▸
printwrites tosys.stdout, which is buffered when not connected to a terminal (a pipe, a log file, a container). Output may appear late or out of order withstderr;print(..., flush=True)forces it, andfile=sys.stderrsends it to the error stream. In services and containers, use theloggingmodule instead ofprint(Topic 7.9). - ▸
Never build SQL or shell commands with f-strings from user input: it opens the door to injection. Use parameters (Topic 15.6). Never put secrets (API keys) into logged f-strings either.
- ▸
Python 3.14 added template strings (t-strings), written
t"...". Unlike an f-string, a t-string does not produce a plainstr: it keeps the fixed text and the inserted values separate, so a library can safely escape the values before joining them. Python 3.14+ only, so it is not available in the browser examples here. - ▸
In logging, prefer
logging.info("user %s", name)over an f-string, so the message is built only if that log level is enabled (Topic 7.9). For AI prompts, an f-string is the usual way to build the prompt text from variables, which is also why prompt injection is a risk: any user text put into the template is data, not trusted instructions.
Remember this
- 1
print(a, b, c)shows its arguments (the values you put inside the brackets) separated by a space and ends with a new line. Two options change that:sep=sets what goes between values,end=sets what goes after the last one.print("a", "b", sep="-")showsa-b, andend=""keeps the next print on the same line.print()alone shows a blank line. Numbers, bools andNoneare converted to text automatically. - 2
An f-string starts with the letter
fbefore the opening quote. Inside,{name}is replaced by the value, and you can put any expression (anything that works out to a value) in the braces:f"{price * qty}". To show a real brace, double it:{{and}}. f-strings are the modern, readable way to build text; they arrived in Python 3.6 and replace the older%and.format()styles in new code. - 3
A format spec follows a colon inside the braces:
{value:spec}. Common ones:.2f(two decimals),,(thousands separator,1,234,567),>8(right-align in 8 spaces),<8(left-align),^8(centre),03d(pad a whole number with zeros to 3 places),bandx(binary and hex),.1%(a fraction as a percent). Topic 3.3 explores the full spec. - 4
The
=specifier is a debugging helper:f"{x=}"showsx=7, andf"{x * 2 = }"showsx * 2 = 14. You see the expression and its value, with no retyping. It arrived in Python 3.8. - 5
Every value has two text forms.
str(x)is the friendly form meant for people, andrepr(x)is the precise form meant for programmers, which shows quotes and escapes.printusesstr. In an f-string,{x!r}asks forrepr.repris what you want when you debug:repr("hi\n")makes the hidden newline visible, andrepr("7")tells text from the number7. - 6
Since Python 3.12 you can reuse the same kind of quote inside an f-string (
f"{"ab" + "cd"}"), and expressions can span lines. Before 3.12 you had to use the other quote kind.printwrites to the screen (standard output), and anything printed in the browser examples on this site appears in the output panel.
Explain it without notes
How do sep and end change what print does?
What is an f-string, and why is it better than joining strings with +?
Explain {value:>8}, {value:.2f} and {value:,}.
What is the difference between str(x) and repr(x)? When would you use repr?
Practice
Set item = "Notebook", price = 45.5, qty = 3, and print Notebook: 3 x 45.50 = 136.50 with an f-string.
Print the number 1234567.891 with commas and one decimal place.
Print the three words red, green, blue on one line separated by | .
Trade-offs
- ↔
**f-strings vs.
.format()and%.** f-strings are clearer and faster, but the template is fixed in the source code. If the template must come from a file or a translation table,.format()or a template library is the right tool. - ↔
**
roundvs. a format spec.**roundchanges the number for later calculations; a format spec changes only how it is displayed. Round once at the end, and format when you print.
Done when you can
Done when you can use
sepandendto shape the output ofprint.Done when you can write an f-string with an expression inside the braces.
Done when you can show a float with 2 decimals, align text, and add thousands commas.
Done when you can use the
=specifier to debug a value.Done when you can explain
strversusrepr.