Context Managers and Resources
Python's with statement and __enter__/__exit__ protocol replaces Java's try-with-resources and AutoCloseable for deterministic resource cleanup.
Introduction
In this lesson, you'll learn about context managers and resources in Python. Coming from Java, you already have a foundation for understanding this concept. We'll build on that knowledge while highlighting the key differences.
In Java, you're familiar with python's with statement and __enter__/__exit__ protocol replaces java's try-with-resources and autocloseable for deterministic resource cleanup..
Python has its own approach to python's with statement and __enter__/__exit__ protocol replaces java's try-with-resources and autocloseable for deterministic resource cleanup., which we'll explore step by step.
The Python Way
Let's see how Python handles this concept. Here's a typical example:
from contextlib import contextmanager
import threading
# Built-in: files, locks, database connections
with open("file.txt") as f: # __exit__ closes file
for line in f:
print(line, end="")
# Multiple resources in one with
with open("src.txt") as src, open("dst.txt", "w") as dst:
dst.write(src.read())
# Custom context manager: __enter__ and __exit__
class Timer:
import time
def __enter__(self):
self._start = self.time.monotonic()
return self # value bound by 'as'
def __exit__(self, exc_type, exc_val, exc_tb):
elapsed = self.time.monotonic() - self._start
print(f"Elapsed: {elapsed:.3f}s")
return False # don't suppress exceptions
with Timer() as t:
sum(range(10_000_000))
# @contextmanager: generator-based shorthand
@contextmanager
def managed_connection(dsn: str):
conn = connect(dsn) # __enter__
try:
yield conn # body of 'with' block runs here
finally:
conn.close() # __exit__
with managed_connection("db.sqlite3") as conn:
conn.execute("SELECT 1")Comparing to Java
Here's how you might have written similar code in Java:
import java.io.*;
import java.sql.*;
public class Resources {
// AutoCloseable: close() called automatically
void readFile(String path) throws IOException {
try (var reader = new BufferedReader(new FileReader(path))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} // reader.close() called here, even on exception
}
// Multiple resources in one try-with-resources
void copy(String src, String dst) throws IOException {
try (var in = new FileInputStream(src);
var out = new FileOutputStream(dst)) {
in.transferTo(out);
}
}
}You may be used to different syntax or behavior.
with open(...) is Python's try-with-resources — __exit__ is called even if an exception is raised
You may be used to different syntax or behavior.
Any class with __enter__/__exit__ is a context manager; Java requires implementing AutoCloseable
You may be used to different syntax or behavior.
@contextmanager turns a generator into a context manager — yield is the 'with body'; try/finally is cleanup
You may be used to different syntax or behavior.
Multiple resources in one 'with' are handled left-to-right on enter, right-to-left on exit
You may be used to different syntax or behavior.
__exit__(type, val, tb) receives exception info; returning True suppresses the exception
Step-by-Step Breakdown
1. with Statement
'with expr as var' calls __enter__ and binds the result to var; __exit__ is called on block exit regardless of exceptions.
try (var f = new BufferedReader(...)) { ... }with open("data.txt", "r") as f:
content = f.read()
# f is closed here, even if an exception was raised2. Multiple Resources
Comma-separated 'with' handles multiple resources. Each is entered left-to-right and exited right-to-left — same as nested with blocks.
try (var in = new FileInputStream(s);
var out = new FileOutputStream(d)) { ... }with open(src) as fin, open(dst, "w") as fout:
fout.write(fin.read())
# Both files closed; fout closed before fin3. Custom Context Manager
__enter__ sets up the resource; __exit__ tears it down. Implement both methods on any class to make it a context manager.
class MyRes implements AutoCloseable {
public void close() { ... }
}class Lock:
def __enter__(self):
acquire()
return self
def __exit__(self, *args):
release()
return False # don't suppress exceptions4. @contextmanager Decorator
Write setup before yield and cleanup in finally. The yield is where the with-block body executes.
// Java: must implement AutoCloseable classfrom contextlib import contextmanager
@contextmanager
def timer(label: str):
import time
start = time.monotonic()
try:
yield # with-block runs here
finally:
print(f"{label}: {time.monotonic()-start:.3f}s")
with timer("sort"):
sorted(range(100_000))Common Mistakes
When coming from Java, developers often make these mistakes:
- with open(...) is Python's try-with-resources — __exit__ is called even if an exception is raised
- Any class with __enter__/__exit__ is a context manager; Java requires implementing AutoCloseable
- @contextmanager turns a generator into a context manager — yield is the 'with body'; try/finally is cleanup
Key Takeaways
- with open(...) as f: auto-closes on exit — Python's equivalent of try-with-resources
- Implement __enter__/__exit__ on any class to make it a context manager
- @contextmanager: yield separates setup from teardown; cleanup goes in finally block
- Multiple resources: with a() as x, b() as y — entered left-to-right, exited right-to-left