skip to content

What does the flush=True argument to Python's print() do?

level: juniorimportance: should knowfreq 45%

answer

  1. print does not reach the terminal directly
  2. text waits somewhere until something empties it
  3. a terminal and a pipe behave differently
  4. flush pushes to the kernel, not the disk
  5. process-wide switches: -u, PYTHONUNBUFFERED

basics

~20 s

flush=True makes print() push its text out of Python's own userspace buffer to the operating system immediately, instead of leaving it there until the buffer fills. Without it, output can sit unseen for a long time.

solid answer

~40 s

print() does not write to the terminal; it writes into `sys.stdout`, a text stream layered over a byte buffer. The text only leaves the process when that buffer fills, when something calls flush, or at normal interpreter shutdown. Passing `flush=True` calls the stream's flush right after the write, so the line goes out at once. On an interactive terminal you rarely notice the difference, because `sys.stdout` is line-buffered there and every newline flushes anyway; the moment stdout is a pipe or a redirected file it becomes block-buffered and a progress line can stay invisible for minutes. `flush=True` is the per-call fix; the process-wide equivalents are the `-u` command-line flag, `PYTHONUNBUFFERED=1`, or `sys.stdout.reconfigure(line_buffering=True)`. Flushing hands bytes to the kernel — it is not a promise that they reached the disk.

code

python · 9 lines
python
import sys
import time

for step in range(3):
    print(f"step {step}", flush=True)
    time.sleep(0.1)

sys.stdout.write("done (no newline)")
sys.stdout.flush()

go deeper

for a junior

Be ready to say that print() writes into a buffer and that flush=True empties it immediately. Knowing the symptom — output that appears only at the end when piped — is what the interviewer is checking.

for a middle

Explain the mechanics: sys.stdout is a text wrapper over a byte buffer, buffering depends on whether stdout is a terminal, and flush=True calls flush() after the write. Name -u and PYTHONUNBUFFERED as the process-wide equivalents.

for a senior

Show the operational judgement: unbuffered output costs a syscall per write, so decide between flushing per line, flushing at checkpoints, or letting a logging handler do it, and know that flushing is not durability.

for a principal

Own the convention: decide whether services standardise on PYTHONUNBUFFERED in the container image or on structured logging, so that no engineer has to remember flush=True, and weigh the throughput cost of that default for high-volume output.

## What print() actually touches A call to `print("hello")` never speaks to a terminal or a file directly. It formats its arguments, joins them with `sep`, appends `end` (a newline by default) and calls `write` on a file object — `sys.stdout` unless you passed `file=`. That object is an `io.TextIOWrapper`: a text layer that encodes `str` to bytes and hands them to a binary buffered layer (an `io.BufferedWriter`), which in turn accumulates bytes and eventually issues a real `write` system call on the underlying `io.FileIO`. So there are three layers, and the interesting one for this question is the middle one: bytes pile up in a userspace buffer that belongs to your process, not to the kernel. ## When the buffer is emptied That buffer is drained in only a handful of situations: it fills up; the stream is line-buffered and you wrote a newline; someone calls `sys.stdout.flush()` (which `print(..., flush=True)` does for you); the file object is closed; or the interpreter shuts down normally and flushes and closes the standard streams. Nothing else empties it — not the passage of time, not the reader of the pipe asking for data. Whether newlines flush depends on how the stream was configured at startup. When CPython starts and finds `sys.stdout` attached to a terminal, it makes it line-buffered, so each `print()` ending in `\n` reaches the terminal immediately and `flush=True` changes nothing you can see. When stdout is a pipe or a redirected file, it is block-buffered instead, with a buffer of `io.DEFAULT_BUFFER_SIZE` — 128 KiB on CPython 3.14, up from 8 KiB in older releases. That is a lot of progress lines to accumulate before anything appears, and it is exactly why a script that looked chatty in your shell goes silent under `| grep`, under a supervisor, or inside a container whose logs you are tailing. ## What flush=True does, precisely `flush=True` calls `flush()` on the target file object after the write. The text layer pushes any pending characters down to the binary layer, and the binary layer issues the `write` syscall. After that the data is in the kernel — visible to any other process that reads the pipe or the file — and out of reach of your process crashing. `print()` has accepted the keyword since Python 3.3; the equivalent explicit form is `sys.stdout.write(...)` followed by `sys.stdout.flush()`. ## What it does not do It is not a durability guarantee. Once the kernel has the bytes they are in the page cache, and a machine that loses power can still lose them; forcing them onto the storage device is a separate call and a separate concern. It also does not change how the stream is configured — the next `print()` without the keyword buffers exactly as before. And it does not affect other file objects: a file you opened yourself with `open()` has its own buffer and its own settings. ## The process-wide alternatives When you find yourself adding `flush=True` to every call, switch the stream instead. `python -u script.py` and the `PYTHONUNBUFFERED=1` environment variable both make the standard streams unbuffered — the usual choice for a containerized service, because it needs no code change. `sys.stdout.reconfigure(line_buffering=True)` (available since Python 3.7) turns line buffering back on for a piped stdout, which is cheaper than fully unbuffered output because it still batches within a line. A logging framework is another answer: `logging.StreamHandler` flushes after each record it emits, which is why a service that logs properly shows output promptly while its stray `print()` calls do not. ## Cost, and why it is not the default everywhere Buffering exists because a `write` syscall costs far more than appending to memory. A loop that prints a million short lines with `flush=True` performs a million syscalls; the same loop buffered performs a few dozen. For human-paced progress output the cost is irrelevant and the visibility is worth everything. For high-rate output it is not, and the sane middle ground is to flush on a schedule, or at meaningful checkpoints, rather than on every line. ## The interview point The reason this comes up so often is that the symptom is misleading: no output looks like a hung or crashed program, when in fact the program is running fine and its words are simply parked in memory. Being able to say "stdout is block-buffered because it is not a tty, so add `flush=True`, run with `-u`, or set `PYTHONUNBUFFERED=1`" is a small piece of knowledge that saves hours of misdirected debugging.

  • Does print(..., flush=True) guarantee the text is safely on disk?
    No. Flushing moves bytes from Python's userspace buffer into the kernel, so other processes can read them and a Python crash cannot lose them. They may still sit in the operating system's page cache; surviving a power loss requires forcing the file's data to the storage device, which is a separate call and a separate design decision.
  • What would you change instead of adding flush=True to every print call?
    Configure the stream once. `python -u` or `PYTHONUNBUFFERED=1` makes the standard streams unbuffered process-wide with no code change, which suits containers. `sys.stdout.reconfigure(line_buffering=True)` restores per-line flushing for a piped stdout at lower cost than fully unbuffered writes. Or route output through a logging handler that flushes each record.
  • Why does the output usually appear anyway at the end of a normal run?
    Normal interpreter shutdown flushes and closes the standard streams, so everything buffered arrives in one burst at exit — which is why a job appears to print nothing and then dump all of its output at once. That safety net disappears if the process is killed abruptly or exits through `os._exit`, which skips cleanup entirely.

Buffering is like writing postcards and dropping them in a shoebox; the box is only emptied into the postbox when it is full. flush=True walks that one postcard to the postbox now.

saying these in an interview costs you the question

  • Thinks print() writes straight to the terminal every time
  • Says flush=True guarantees the data is on disk
  • Reads missing output as proof the code never ran that line
  • Blames the operating system for holding the text
  • Adds flush=True to a million-iteration loop without considering syscall cost
  • Confuses flushing a stream with closing it

context