24 · Reading and writing files
open(), modes, the with statement, reading line by line, and why always specifying encoding='utf-8' saves you from Windows surprises.
`open(path, mode)` returns a **file object** — a handle through which you read or write bytes. The `with` statement is a **context manager** that guarantees the file is closed the moment the indented block exits, even if an exception is raised inside it.
Without this:
Without explicit file handles, you have no way to read training data from disk, write metric logs, or save and load serialised models. Every ML pipeline starts and ends with I/O.
To read or write a file you first need a handle — an object that represents the open connection to the file on disk. open(path, mode) creates that handle. The mode string controls whether you're reading ("r"), writing ("w", truncates first), appending ("a"), or using a binary variant ("rb", "wb"). The default mode is "r". Leaving a file open longer than necessary wastes OS resources and can corrupt data if the program crashes before flushing. The with statement solves this automatically: no matter how the block exits — normal return, exception, break — Python calls f.close() for you.
Python (in browser)
`f.writelines(iterable)` writes each item without adding separators — you must include `\n` yourself. `f.read()` returns the whole file as a single string; `f.readlines()` returns a list of lines, each still carrying its trailing newline.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
Python (in browser)
Iterating `for line in f:` reads one line at a time — Python never loads the whole file into memory. Always call `.rstrip()` (or `.rstrip("\n")`) to strip the trailing newline that `readline` preserves; forgetting it causes double-spaced output when you `print`.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
Python (in browser)
The `with` statement calls `f.__exit__()` at block end, which flushes any buffered bytes and closes the OS file descriptor. Without it, a crash between `open()` and `close()` leaks the file descriptor — on long-running processes this exhausts the OS limit and raises `OSError: Too many open files`.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
What does opening a file with mode `"a"` do?
- `open(path, mode, encoding="utf-8")` returns a file handle. Modes: `"r"` read, `"w"` write (truncates), `"a"` append, `"rb"`/`"wb"` binary. Always specify `encoding` for text files.
- Use `with open(...) as f:` — the context manager guarantees `f.close()` is called even if an exception is raised inside the block.
- Iterate `for line in f:` for memory-efficient line-by-line reading. Call `.rstrip()` to strip the trailing newline.
Training data lives in CSV, JSONL, or plain-text files on disk — `open()` is the gateway. Metric logging writes loss and accuracy after each epoch to a `.log` file opened in `"a"` mode. Model checkpoints use binary mode under the hood: `torch.save(state_dict, "checkpoint.pt")` and numpy's `np.save("weights.npy", arr)` both write raw bytes. `encoding="utf-8"` is mandatory when reading multilingual text corpora for NLP tasks.
If you remove it: Without file I/O, every dataset would need to be hard-coded in the script and every trained model would be lost at process exit. Files are the persistence layer of every ML pipeline.