print() is the first function almost everyone meets in Python, and it does more than put characters on the screen. It accepts any number of arguments, converts each one to its string form with str(), joins them with a separator, appends a terminator, and writes the result to a file-like object. Every one of those behaviours can be customised with a keyword argument, which is why a single print() call is usually enough even for structured output.
The four keyword arguments worth memorising are sep (the string placed between arguments, default a single space), end (the string placed at the end, default a newline), file (the destination, default sys.stdout) and flush (whether to flush the buffer immediately, default False). Learning them unlocks compact logging, progress counters, CSV-style output and writing to standard error without any extra imports.
For formatted text, Python offers three styles in ascending order of readability. The oldest is %-formatting ("%s is %d" % (name, age)), borrowed from C and still found in the standard library. Next is str.format() ("{} is {}".format(name, age)), which supports named placeholders. The modern choice is the f-string (f"{name} is {age}"), introduced in Python 3.6, which evaluates expressions directly and supports rich format specifiers such as width, alignment and precision.
There are two ways to turn an object into text: str() returns the human-friendly form and is what print() calls; repr() returns an unambiguous form useful for debugging. A list will print ['a', 'b'] because its __str__ falls back to __repr__, but a datetime will print '2026-04-21 12:00:00' for str() and 'datetime.datetime(2026, 4, 21, 12, 0)' for repr(). Pick the right one for the audience — users get str, other developers get repr.
Finally, remember that print() writes text. Diagnostics and errors should go to sys.stderr, not sys.stdout, so that real output can be piped to the next command while messages still appear on the screen. For structured data destined for another program, write JSON or CSV rather than formatting by hand; the Python standard library covers both and avoids every escaping bug.
The four keywords of print()
Change sep to make print() a tiny CSV writer: print("a", "b", "c", sep=",") outputs a,b,c. Change end to build a progress line that updates in place: print(step, end="\r", flush=True). Redirect with file=sys.stderr to send diagnostic lines where they belong. These four keywords replace a surprising amount of manual string assembly.
F-strings and format specifiers
An f-string's format specifier follows a colon: f"{value:,.2f}" formats a number with a thousands separator and two decimal places. Common specifiers are >, < and ^ for alignment, 0<width> for zero padding, % for percentages and e for scientific notation. The !r and !s conversion flags call repr() or str() explicitly, useful when logging a value alongside its type.
Python 3.8 added the = debug flag: f"{name=}" prints name='Ada', which is a huge time saver while investigating a bug. Keep f-strings short; if the expression grows longer than the surrounding text, extract it into a local variable first.
These tools cover almost all day-to-day output needs — from plain print lines to JSON and CSV.
| Tool | Purpose |
|---|---|
print()built-in | Writes objects with optional sep, end, file and flush. |
f-stringsyntax | Embeds expressions and format specs inside a string literal. |
str.format()method | Templated formatting with named or positional placeholders. |
repr()built-in | Returns the unambiguous representation used for debugging. |
sys.stderrfile object | Destination for diagnostic and error messages. |
json.dumps()function | Serialises Python data to a JSON string. |
csv.writerclass | Writes rows of data with proper quoting and escaping. |
pprint()function | Pretty-prints nested containers with sensible line breaks. |
Displaying Output in Python code example
The script below prints the same report in four styles so you can compare them side by side.
# Lesson: Displaying Output in Python
# Goal: show the four styles (print kw args, f-string, str.format, and a
# JSON line) produce output that is readable for humans and machines.
import json
import sys
people = [
{"name": "Ada", "age": 36, "score": 97.5},
{"name": "Grace", "age": 85, "score": 88.0},
{"name": "Linus", "age": 54, "score": 73.25},
]
# 1) Headline using sep=/end= to build a separator line
print("name", "age", "score", sep=" | ")
print("-" * 22)
# 2) Table rows using an f-string with alignment and precision
for p in people:
print(f"{p['name']:<6} | {p['age']:>3} | {p['score']:6.2f}")
# 3) Diagnostic line sent to stderr so it won't pollute stdout
print(f"processed {len(people)} rows", file=sys.stderr)
# 4) Machine-readable JSON line written last, one per record
for p in people:
print(json.dumps(p, sort_keys=True))
Four distinct print calls, each showing one technique:
1) sep=" | " rebuilds the header without string concatenation.
2) F-string format specs `<6`, `>3`, `6.2f` align columns cleanly.
3) file=sys.stderr keeps diagnostics off the data stream.
4) json.dumps gives downstream tools a stable, parseable format.
Play with the keyword arguments in isolation before changing the main example.
# Example A: progress counter that overwrites the same line
import time
for i in range(1, 6):
print(f"step {i}/5", end="\r", flush=True)
time.sleep(0.01)
print() # move to a fresh line when done
# Example B: the f-string debug flag shows variable names automatically
temperature = 21.4
humidity = 0.58
print(f"{temperature=}, {humidity=:.0%}")
# prints: temperature=21.4, humidity=58%
Sanity checks that would fail if the formatting is accidentally changed.
assert f"{3.14159:.2f}" == "3.14"
assert f"{'x':>4}" == " x"
assert f"{42:05d}" == "00042"
assert json.loads(json.dumps(people[0])) == people[0]
Running the script prints the table on stdout and a single diagnostic line on stderr:
name | age | score
----------------------
Ada | 36 | 97.50
Grace | 85 | 88.00
Linus | 54 | 73.25
{"age": 36, "name": "Ada", "score": 97.5}
{"age": 85, "name": "Grace", "score": 88.0}
{"age": 54, "name": "Linus", "score": 73.25}