I/O Operations in Python
Learn console input and output, file reading and writing, formatted output, and I/O error handling in Python with Docker-ready examples
Input and output (I/O) is how a program communicates with the outside world—printing results to the terminal, reading what a user types, and persisting data to files. You already met print() in Hello World; this tutorial goes deeper into formatting that output, reading from standard input, and working with files.
Python’s dynamic, “batteries included” philosophy shows through clearly in I/O. There are no stream classes to instantiate, no manual buffer management, and no file handles to remember to close if you use the idiomatic with statement. Files are ordinary objects, print() and input() are built-in functions, and errors surface as exceptions you can catch. As a multi-paradigm language, Python lets you write I/O in a straightforward procedural style while still leaning on context managers—a small object-oriented convenience that guarantees cleanup.
In this tutorial you’ll learn how to format console output with f-strings and the print() parameters, read typed input and convert it to the right type, write and read files safely with the with statement, and handle common I/O errors gracefully.
Formatted Console Output
print() does far more than emit a fixed string. It accepts multiple arguments, separator and terminator options, and works hand-in-hand with Python’s formatting mini-language.
Create a file named formatted_output.py:
| |
Here {pi:.2f} rounds the float to two decimal places, and {age:05d} pads the integer to a width of five with leading zeros. The sep parameter controls what goes between arguments, while end controls what goes after the last one—setting end="" or another string lets you keep output on the same line.
Reading Input from the User
The built-in input() function reads one line from standard input. It always returns a string, so numeric input must be converted explicitly—a direct consequence of Python’s strong typing, which won’t silently treat "36" as the number 36.
Create a file named read_input.py:
| |
Because these programs wait for input, you’ll typically pipe data into them when running non-interactively (shown in the Docker section below). If you ran this without providing the age as a number—say you typed thirty—the int() call would raise a ValueError, which the next section shows how to handle.
Writing and Reading Files
Files are opened with open(), which returns a file object. The idiomatic pattern is the with statement (a context manager): it guarantees the file is closed automatically when the block ends, even if an error occurs. The mode string selects the operation—"w" writes (creating or overwriting), "a" appends, and "r" reads.
Create a file named file_io.py:
| |
Reading the whole file with .read() is convenient for small files, but iterating line by line (for line in f) is the memory-friendly choice for large ones—it never loads the entire file at once. Here enumerate(f, start=1) numbers each line, and line.rstrip() trims the trailing newline that comes with each line of text.
Handling I/O Errors
I/O is where things go wrong: files go missing, permissions are denied, and user input is malformed. Python surfaces these as exceptions, so the natural way to handle them is try/except. This keeps the happy path readable while still recovering from failure.
Create a file named io_errors.py:
| |
Catching the specific exception (FileNotFoundError, ValueError) rather than a bare except means you only handle the failures you expect and let genuine bugs surface. FileNotFoundError and PermissionError are both subclasses of OSError, so catching OSError covers the broad category of file-system problems. The !r conversion in the f-string shows each value with quotes, making it obvious whether you’re looking at the string '42' or something else.
Running with Docker
| |
The volume mount (-v $(pwd):/app) maps your current directory into the container, so files written by file_io.py—like notes.txt and output.txt—appear in your working directory after the run.
Expected Output
Running formatted_output.py:
Name: Ada, Age: 36
Pi rounded: 3.14
Zero-padded: 00036
Name: Ada, Age: 36
a-b-c
no newline here >>> same line
Running read_input.py with the piped input Ada and 36:
Enter your name: Hello, Ada!
Enter your age: Next year you will be 37
Running file_io.py:
Full contents:
First line
Second line
Third line
1: First line
2: Second line
3: Third line
Running io_errors.py:
Error: the file 'missing.txt' was not found
Parsed '42' -> 42
Could not parse 'hello' as an integer
Parsed '17' -> 17
Write complete
Key Concepts
print()is configurable — Use f-strings for interpolation, format specs like:.2fand:05dfor numbers, and thesep/endparameters to control spacing and line endings.input()always returns a string — Convert withint(),float(), etc. when you need a number; Python’s strong typing won’t do it for you.- Use
with open(...)for files — The context manager closes the file automatically, even if an exception is raised, so you never leak file handles. - Mode strings pick the operation —
"r"reads,"w"overwrites,"a"appends; add"b"for binary data. - Iterate files line by line for large data —
for line in fstreams one line at a time instead of loading the whole file into memory with.read(). - I/O errors are exceptions — Catch specific types like
FileNotFoundErrorandValueError;OSErroris the shared base class for file-system failures. - Prefer specific
exceptclauses — Handle only the errors you expect so real bugs aren’t silently swallowed.
Running Today
All examples can be run using Docker:
docker pull python:3.13-alpine
Comments
Loading comments...
Leave a Comment