Yesterday's pattern: open, do work, close. The problem — what if an error fires between open and close?
f = open("data.txt", "w")
f.write(some_value) # if some_value is wrong type, raises TypeError
f.close() # never reached — file leaksThe script crashes with a half-open file. The OS eventually cleans it up, but in the meantime my buffered writes might never hit disk.
Right. Python's with statement solves this. It runs your code inside a context, and guarantees a cleanup step at the end — even if the body raises:
with open("data.txt", "w") as f:
f.write(some_value)
# f.close() is called here automatically — error or no errorNo f.close() line at all?
Right. The with block owns it. When the block exits — normal exit, return, exception, anything — Python calls f.close() for you. Same guarantee Java's try-with-resources gives, same guarantee Rust's Drop gives. The shape:
with open("x") as f: # 1. open and bind to f
use(f) # 2. body runs
# 3. Python closes f here, no matter what happened in step 2And from now on we always use with for files?
Always. The previous two lessons showed the manual shape so you know what with is doing. From here on every file operation in this track uses with.
with — the canonical file-access patternwith open("path") as f:
text = f.read()
# f is closed here, even if an exception fired inside the blockThree things to notice:
with block owns the lifecycle.f is bound for the duration of the block. Outside the block, f is still in scope but the file is closed — calling f.read() after the block raises ValueError: I/O operation on closed file.f.read() raises, the close still happens.with open("data.txt") as f:
text = f.read()
print(text) # f is closed but text is still in memorywith open("out.txt", "w") as f:
f.write("alpha\n")
f.write("beta\n")with open("log.txt", "a") as f:
f.write("new entry\n")with open("big.txt") as f:
for line in f:
process(line)Memory-efficient — only one line in RAM at a time. Works even on files much bigger than RAM.
withComma-separate them — useful for read-from-A, write-to-B:
with open("in.txt") as src, open("out.txt", "w") as dst:
dst.write(src.read().upper())Both files close when the block exits, in reverse order.
with, in generalwith works on any context manager — a value that implements __enter__ and __exit__. Files are the most common; database connections, locks, and temporary directories also use the same pattern. You won't write your own context managers in this track (that's Mastery), but recognizing the shape lets you read documentation that uses it.
Yesterday's pattern: open, do work, close. The problem — what if an error fires between open and close?
f = open("data.txt", "w")
f.write(some_value) # if some_value is wrong type, raises TypeError
f.close() # never reached — file leaksThe script crashes with a half-open file. The OS eventually cleans it up, but in the meantime my buffered writes might never hit disk.
Right. Python's with statement solves this. It runs your code inside a context, and guarantees a cleanup step at the end — even if the body raises:
with open("data.txt", "w") as f:
f.write(some_value)
# f.close() is called here automatically — error or no errorNo f.close() line at all?
Right. The with block owns it. When the block exits — normal exit, return, exception, anything — Python calls f.close() for you. Same guarantee Java's try-with-resources gives, same guarantee Rust's Drop gives. The shape:
with open("x") as f: # 1. open and bind to f
use(f) # 2. body runs
# 3. Python closes f here, no matter what happened in step 2And from now on we always use with for files?
Always. The previous two lessons showed the manual shape so you know what with is doing. From here on every file operation in this track uses with.
with — the canonical file-access patternwith open("path") as f:
text = f.read()
# f is closed here, even if an exception fired inside the blockThree things to notice:
with block owns the lifecycle.f is bound for the duration of the block. Outside the block, f is still in scope but the file is closed — calling f.read() after the block raises ValueError: I/O operation on closed file.f.read() raises, the close still happens.with open("data.txt") as f:
text = f.read()
print(text) # f is closed but text is still in memorywith open("out.txt", "w") as f:
f.write("alpha\n")
f.write("beta\n")with open("log.txt", "a") as f:
f.write("new entry\n")with open("big.txt") as f:
for line in f:
process(line)Memory-efficient — only one line in RAM at a time. Works even on files much bigger than RAM.
withComma-separate them — useful for read-from-A, write-to-B:
with open("in.txt") as src, open("out.txt", "w") as dst:
dst.write(src.read().upper())Both files close when the block exits, in reverse order.
with, in generalwith works on any context manager — a value that implements __enter__ and __exit__. Files are the most common; database connections, locks, and temporary directories also use the same pattern. You won't write your own context managers in this track (that's Mastery), but recognizing the shape lets you read documentation that uses it.
Create a free account to get started. Paid plans unlock all tracks.