Python Tutorial: File Objects - Reading and Writing to Files
Based on Corey Schafer's video on YouTube. If you like this content, support the original creators by watching, liking and subscribing to their content.
Use with open(...) to guarantee files close automatically, preventing file-descriptor leaks and errors.
Briefing
Python file objects hinge on two practical choices: opening files in the right mode and managing their lifecycle safely. Using open() without cleanup can leave files open and eventually exhaust the system’s file descriptors, so the tutorial pushes the with context manager as the default pattern. Inside that with block, the file object stays usable; once the block ends, the file is automatically closed—even if the variable still exists—so attempts to read afterward raise an “I/O operation on a closed file” error.
Once a file is open for reading (mode “r”), the tutorial walks through several ways to pull data out. f.read() loads the entire contents into memory, which is fine for small files but risky for very large ones. f.readlines() returns a list of lines, preserving newline characters, while f.readline() advances one line at a time. For large files, iterating directly—“for line in f”—streams line by line without loading everything at once. When more control is needed, f.read(size) reads a fixed number of characters and advances the file pointer accordingly. The pointer position can be inspected via f.tell(), and repositioned with f.seek(). A chunked read loop uses f.read(size) repeatedly until it returns an empty string at end-of-file, avoiding both memory blowups and hardcoded assumptions about file length.
Writing follows the same mode-driven logic. Opening a file in read mode prevents writing and triggers an error (“not writable”), so writing requires mode “w” (overwrite/create) or “a” (append). After writing, the file pointer advances just like reading, so consecutive writes append at the current position. f.seek() can reposition during writing too, but it can be confusing: seeking to the start and writing again overwrites only the portion covered by the new content, leaving any remaining bytes from the earlier write intact.
To combine reading and writing, the tutorial demonstrates copying files using two context managers at once: open the source in read mode and open the destination in write mode, then write each line from the source to the target. The same approach fails for images when treated as text, producing a UnicodeDecodeError. The fix is binary mode: add “b” to the read/write modes (e.g., “rb” and “wb”) so the code transfers bytes rather than decoding text. Finally, it shows chunk-based copying for large binary files: read a fixed-size block (e.g., 496 bytes) in a loop and write each chunk until the read returns an empty result, producing an exact copy without excessive memory use.
Overall, the core takeaway is that reliable file handling in Python comes down to correct modes, context-managed cleanup, and choosing the right read/write strategy—line iteration for text, binary mode for images, and chunked loops for large data.
Cornell Notes
File handling in Python depends on opening files with the correct mode and using context managers to ensure automatic cleanup. A file opened with open() must be closed; otherwise, open handles can accumulate and trigger file-descriptor errors. Inside a with open(...) block, reading methods like read(), readlines(), readline(), and line iteration work differently—read() loads everything, while iteration streams line by line. For large files, f.read(size) plus a loop reads fixed-size chunks until end-of-file (empty string). Writing requires write mode (“w”) or append mode (“a”); writing in read mode fails. For images and other non-text data, use binary mode (“rb”/“wb”) and copy in chunks to avoid decoding errors and memory issues.
Why does code that reads from a file need a context manager, even if it “works” without one?
When should f.read(), f.readlines(), f.readline(), or iterating “for line in f” be used?
How does chunked reading with f.read(size) prevent memory problems?
What changes when switching from reading to writing files?
Why does copying an image fail in text mode, and how is binary mode different?
How does chunked binary copying work for large files?
Review Questions
- What happens if you try to read from a file object after exiting its with open(...) block?
- Compare f.read() and iterating “for line in f” in terms of memory usage and suitability for large files.
- When copying a JPEG, why is binary mode required, and what modes should be used for reading and writing?
Key Points
- 1
Use with open(...) to guarantee files close automatically, preventing file-descriptor leaks and errors.
- 2
Open files with the correct mode: “r” for reading, “w” for overwrite/create writing, “a” for append, and “rb”/“wb” for binary data like images.
- 3
Choose reading methods based on size: f.read() for small files, line iteration for large text files, and f.read(size) loops for controlled chunk processing.
- 4
Track and control the file pointer with f.tell() and f.seek(); seeking changes where the next read/write occurs.
- 5
Writing in read mode fails; writing advances the file pointer, so consecutive writes continue where the last one left off.
- 6
Copy files by opening source for reading and destination for writing, then transferring data (lines for text, bytes for binary).
- 7
For large binary files, copy in fixed-size chunks in a loop until the read returns empty, ensuring exact output without excessive memory use.