Example 1: Basic Mechanics
Consider the following class definition and its usage:
class ResourceHandler:
def __init__(self):
print('Object initialized')
def __enter__(self):
print('Setup phase')
def __exit__(self, exc_type, exc_value, traceback):
print('Teardown phase')
resource = ResourceHandler()
with resource:
print('Main logic running')
Output:
Object initialized
Setup phase
Main logic running
Teardown phase
When an object defines both __enter__ and __exit__ methods, it is considered a context manager. The with construtc interacts with this protocol by executing the __enter__ method before the block starts and the __exit__ method immediately after the block finishes.
Example 2: Exception Handling
Context managers integrate tightly with exception handling. Observe the behavior when an error occurs inside the block:
class SafeHandler:
def __init__(self):
print('Ready')
def __enter__(self):
print('Start')
def __exit__(self, exc_type, exc_value, traceback):
print('Cleanup')
# return True
handler = SafeHandler()
with handler:
raise ValueError("Something went wrong")
print('This will not print')
If the __exit__ method returns a truthy value (like True), the exception is suppressed. If it returns None or False (the default), the exception propagates after cleanup. This mimics the behavior of a try...except...finally block but with cleaner syntax.
Example 3: The 'as' Keyword
The __enter__ method can return a value that is bound to a variable using the as keyword:
class Connection:
def __enter__(self):
print('Opening connection')
return self # The returned value is assigned to 'conn'
def __exit__(self, exc_type, exc_value, traceback):
print('Closing connection')
with Connection() as conn:
print(f'Using connection: {conn}')
If __enter__ does not explicitly return a value, the target variable (e.g., conn) will be None. The target can be a single varible or a tuple enclosed in parentheses.
Example 4: File Operations
The most common use case is managing file streams. The built-in open() function returns a file object that acts as a context manager.
with open("/tmp/data.txt") as file_handle:
content = file_handle.read()
This ensures the file is automatically closed when the block exits, even if an error occurs. Without with, you would need a manual try/finally structure:
file_handle = open('data.txt')
try:
content = file_handle.read()
finally:
file_handle.close()
Several standard library modules support this protocol, including threading and decimal.
Common Context Managers
Besides files, other standard tools that implement the context management protoclo include:
threading.Lockthreading.RLockthreading.Conditionthreading.Semaphoredecimal.Context