Command Palette

Search for a command to run...

Hectal
PHASE 1Beginner ~26 min· topic 9 of 11

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.

print
A function that shows values on the screen.
sep
The option of print that sets the text placed between values.
end
The option of print that sets the text placed after the last value.
f-string
A string starting with f where {} 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.

main.pywhole filepython
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 line

02Your 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.

main.pywhole filepython
name = "Asha"
qty = 3
print(f"Name: {name}")
print(f"Items: {qty}, double is {qty * 2}")
Your first f-stringdiagram
Rendering diagram…

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).

main.pywhole filepython
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 ff

04Format 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.

main.pywhole filepython
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.

main.pywhole filepython
x = 7
print(f"{x=}")          # x=7
print(f"{x * 2 = }")    # x * 2 = 14

06str 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.

main.pywhole filepython
text = "hi\nthere"
print(text)            # two lines
print(repr(text))      # 'hi\nthere'
print(repr("7"), repr(7))   # '7' 7

Try it yourself

  1. 1

    Make a receipt line

    Set item = "Pen" and price = 12.5. Print Pen 12.50 using one f-string with :<10 for the item and :.2f for the price.

  2. 2

    Debug with =

    Set a = 4 and b = 9. Print f"{a=} {b=} {a * b=}" and look at what you get.

  3. 3

    See the invisible

    Set word = "cat " (with a space at the end). Print word and then repr(word). Which one shows the space?

Code & diagrams

print options and f-string format specs New tab

Change the widths and the number of decimals and watch the output change.

Sign in to run this example in your browser.

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 Asha
The = specifier, str and repr Python 3.8+ New tab

The = specifier needs Python 3.8 or later. Compare the two ways of showing text with a new line in it.

Sign in to run this example in your browser.

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' 7
Reusing quotes inside an f-string Python 3.12+ New tab

Before Python 3.12 the first line was a SyntaxError. The second form works on every version.

Sign in to run this example in your browser.

Expected output

abcd
single
A receipt table with format specs New tab

A 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}.

Sign in to run this example in your browser.

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 0xff

Break 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.

main.pywhole filepython
name = "Asha"
print(f"Hello, {nmae}")
terminal
$ python main.py
── what you'll see ──
Traceback (most recent call last):
File "main.py", line 2, in <module>
print(f"Hello, {nmae}")
^^^^
NameError: name 'nmae' is not defined

Break #2

A format code that does not match the type

Ask for the d (whole number) format on a decimal number.

main.pywhole filepython
score = 91.456
print(f"{score:.2d}")
terminal
$ python main.py
── what you'll see ──
Traceback (most recent call last):
File "main.py", line 2, in <module>
print(f"{score:.2d}")
^^^^^^^^^^^
ValueError: Unknown format code 'd' for object of type 'float'

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}" is 2.67 (the float is slightly below 2.675), and ties go to even: f"{0.5:.0f}" is 0 and f"{2.5:.0f}" is 2. Other specs worth knowing: _ as a separator ({n:_}), + to always show the sign ({x:+.1f}), #x for 0xff, e and g for scientific and general notation, and !s, !r, !a conversions. {x=} uses repr, so f"{name=}" shows name='Asha' with quotes.

  • ▸

    print writes to sys.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 with stderr; print(..., flush=True) forces it, and file=sys.stderr sends it to the error stream. In services and containers, use the logging module instead of print (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 plain str: 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. 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="-") shows a-b, and end="" keeps the next print on the same line. print() alone shows a blank line. Numbers, bools and None are converted to text automatically.

  2. 2

    An f-string starts with the letter f before 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. 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), b and x (binary and hex), .1% (a fraction as a percent). Topic 3.3 explores the full spec.

  4. 4

    The = specifier is a debugging helper: f"{x=}" shows x=7, and f"{x * 2 = }" shows x * 2 = 14. You see the expression and its value, with no retyping. It arrived in Python 3.8.

  5. 5

    Every value has two text forms. str(x) is the friendly form meant for people, and repr(x) is the precise form meant for programmers, which shows quotes and escapes. print uses str. In an f-string, {x!r} asks for repr. repr is what you want when you debug: repr("hi\n") makes the hidden newline visible, and repr("7") tells text from the number 7.

  6. 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. print writes to the screen (standard output), and anything printed in the browser examples on this site appears in the output panel.

Explain it without notes

01

How do sep and end change what print does?

02

What is an f-string, and why is it better than joining strings with +?

03

Explain {value:>8}, {value:.2f} and {value:,}.

04

What is the difference between str(x) and repr(x)? When would you use repr?

Practice

01

Set item = "Notebook", price = 45.5, qty = 3, and print Notebook: 3 x 45.50 = 136.50 with an f-string.

02

Print the number 1234567.891 with commas and one decimal place.

03

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.

  • ↔

    **round vs. a format spec.** round changes 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 sep and end to shape the output of print.

  • 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 str versus repr.