Python Context Managers
Table of Contents + β
In the last lesson you learned about File Paths. Now you can point Python at the right file. There is one more thing about working with files that matters a lot. Every file you open has to be closed again. If you forget, things can go wrong in quiet ways. So how do you make sure a file always gets closed, even when something breaks? That is the job of a context manager.
π€ Why You Need This
Opening a file is easy. The hard part is remembering to close it. And it is easy to forget.
Here is the pain. When you open a file, Python sets aside some resources to work with it. A resource is something your program borrows from the computer. It could be a slot in memory or a handle to the file on disk. If you never close the file, you never give that resource back.
That causes real problems:
- Writes can get lost. Python often holds what you write in memory for a moment before saving it to disk. Closing the file makes sure everything actually gets written. Forget to close, and the last bit may never reach the disk.
- Resources leak. Each open file uses one of a limited number of file handles. Leave too many open and your program runs out and crashes.
- The file may stay locked. On some systems an open file cannot be opened or moved by anything else until it is closed.
The fix is the with statement. It opens the file for you. Then it closes the file by itself the moment you are done. You cannot forget, because it is automatic.
π§© The Manual Way, and Where It Fails
Before the clean way, let us see the old way. That way you understand the problem we are solving.
You can open a file, work with it, then close it by hand.
f = open("notes.txt", "w")f.write("Hello from Python")f.close()Walk through it:
open(...)opens the file and hands back a file object. We store it inf.f.write(...)writes some text into the file.f.close()closes the file and saves everything to disk.
This works. But it has a weak spot. Look what happens if something goes wrong between opening and closing.
f = open("notes.txt", "w")f.write("Hello from Python")result = 10 / 0 # this line crashesf.close() # this never runsThat 10 / 0 crashes the program. So Python jumps away before it ever reaches f.close(). The file is left open. The text you wrote may never be saved. This is exactly the kind of bug that is hard to spot, because the code looks fine.
Output
Traceback (most recent call last): File "main.py", line 3, in <module> result = 10 / 0ZeroDivisionError: division by zeroThe program stopped on the error. So f.close() got skipped, and the file stayed open.
β The with Statement
Here is the same job done the recommended way. This opens the file, writes to it, and closes it automatically.
with open("notes.txt", "w") as f: f.write("Hello from Python")Read it like this:
open("notes.txt", "w")opens the file, just like before.as fgives the open file a name, so inside the block you call itf.- Everything indented under the
withline is the block where the file is open and ready to use. - When the block ends, Python closes the file for you. You write no
close()at all.
The big win is that last point. The moment the indented block finishes, the file is closed. So you never have to remember it.
Note
Here is a handy way to think about it. The with block is like borrowing a library book. The context manager is the librarian who quietly takes the book back the second you walk out the door. You never have to return it yourself.
π‘οΈ It Closes Even When Things Break
This is the part that makes with worth it. The file closes even if an error happens inside the block.
Here is the crashing example from before, now written with with.
with open("notes.txt", "w") as f: f.write("Hello from Python") result = 10 / 0 # this line still crashesThe 10 / 0 still crashes the program. But this time, before Python leaves, the context manager steps in and closes the file properly. So whatever was written gets saved, and the resource is handed back. The program still shows the error, but the file is safe.
Output
Traceback (most recent call last): File "main.py", line 3, in <module> result = 10 / 0ZeroDivisionError: division by zeroSame error on screen. The difference is invisible but important. The file was closed cleanly on the way out. With the manual open and close, it would have been left hanging open.
π Reading a File the Same Way
with is not only for writing. You use the exact same shape to read. This opens a file, reads everything in it, and prints it.
First, imagine we already saved a file called notes.txt with this inside it:
Line oneLine twoLine threeNow we read it back.
with open("notes.txt", "r") as f: content = f.read()
print(content)Walk through it:
open("notes.txt", "r")opens the file in read mode. The"r"means read.f.read()reads the whole file into the variablecontent.- The block ends, so the file closes on its own.
- Then
print(content)shows what we read. Notice this line is outside the block. That is fine, because we already have the text incontent.
Output
Line oneLine twoLine threeSame clean pattern, whether you are reading or writing. Open it with with, do your work inside, and let it close itself.
π Manual close vs with
Keep this small comparison nearby until the with habit sticks.
| Question | Manual open/close | with statement |
|---|---|---|
| Do you call close() yourself? | Yes, every time | No, it is automatic |
| Closes if an error happens? | No, close() gets skipped | Yes, it always closes |
| Easy to forget? | Yes, very easy | Nothing to forget |
| Recommended? | No | Yes, this is the standard way |
β οΈ Common Mistakes
A few things trip people up the first time. Watch for these.
- Still calling
close()inside awithblock. You do not need to, and it just adds noise.
# β Avoid: with already closes the file, this is pointlesswith open("notes.txt", "w") as f: f.write("Hi") f.close()
# β
Good: let with handle the closingwith open("notes.txt", "w") as f: f.write("Hi")- Using the file after the block ends. Once the block finishes, the file is closed. Trying to read or write it then will crash.
# β Avoid: f is already closed out herewith open("notes.txt", "r") as f: content = f.read()f.read() # crashes, the file is closed
# β
Good: do your file work inside the blockwith open("notes.txt", "r") as f: content = f.read()print(content) # using the text you saved, not the file- Going back to the manual
openandclosehabit out of routine. There is almost no reason to open a file withoutwithin everyday code.
β Best Practices
Keep these small habits and file handling stays safe.
- Always reach for
with open(...) as f:when working with files. It is the standard, safe way. - Keep the block short. Do only the file reading or writing inside it, then work with the data outside.
- If you read a file, store what you need in a variable inside the block. Then use that variable after. Do not keep poking at the file once the block is done.
- Pick the right mode. Use
"r"to read and"w"to write, and pass it as the second argument toopen.
π§© What Youβve Learned
β Every file you open must be closed, or you risk lost writes and leaked resources.
β
The manual open and close way skips close() if an error happens partway, leaving the file open.
β
The with statement is a context manager that opens the file and closes it automatically when the block ends.
β It closes the file even when an error is raised inside the block, which is why it is the recommended way.
β
The same with open(...) as f: shape works for both reading and writing.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
What does the with statement do automatically when its block ends?
Why: A context manager closes the file by itself when the with block finishes, so you never call close() yourself.
- 2
Why is manual open/close risky if an error happens between them?
Why: When an error is raised before close() runs, Python jumps away and close() never runs, leaving the file open.
- 3
What does the as f part of with open("notes.txt", "r") as f: do?
Why: as f gives the open file object a name so you can refer to it as f inside the with block.
- 4
What happens if you try to read from f after the with block has ended?
Why: Once the with block ends the file is closed, so using f to read or write afterwards raises an error.
π Whatβs Next?
You can now handle files safely without ever worrying about closing them. Next we step into a whole new way of organizing code around objects. This is how most real programs are built.