with

Updated March 16, 2026 · Keywords
keyword context-manager resource-management file-handling

The with statement in Python simplifies resource management by handling setup and cleanup automatically. It ensures that resources like files, database connections, or locks are properly acquired and released, even if errors occur during execution.

Syntax

with expression as variable:
    # Block where variable is available

The expression must return a context manager — an object that implements __enter__ and __exit__ methods.

Basic Examples

Opening a File

The most common use case is file handling:

with open("example.txt", "w") as f:
    f.write("Hello, World!")
# File is automatically closed when exiting the block

Reading a File

with open("example.txt", "r") as f:
    content = f.read()
    print(content)
# File is automatically closed after reading

The file is guaranteed to be closed, even if an error occurs while reading.

How It Works

Under the Hood

When you use with, Python calls:

  1. __enter__() — runs at the start of the block, returns the value bound to as
  2. Your code executes inside the block
  3. __exit__() — runs when exiting the block, even if an exception occurred
class MyContext:
    def __enter__(self):
        print("Entering context")
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("Exiting context")
        return False

with MyContext() as ctx:
    print("Inside block")
# Entering context
# Inside block
# Exiting context

The as Clause

The as clause binds the result of __enter__() to a variable:

with open("file.txt") as f:
    # f is the file object returned by open().__enter__()
    pass

You can skip the as clause if you don’t need the bound value:

with open("file.txt"):
    # File is open, but no variable bound
    pass

Multiple Context Managers

Stacking Multiple With Statements

with open("input.txt") as infile:
    with open("output.txt", "w") as outfile:
        outfile.write(infile.read())

Python 3.1+ — Multiple Context Managers

Python 3.1 introduced a more concise syntax:

with open("input.txt") as infile, open("output.txt", "w") as outfile:
    outfile.write(infile.read())

Python 3.10+ — Parenthesized Context Managers

Python 3.10 allows line breaks:

with (
    open("input.txt") as infile,
    open("output.txt", "w") as outfile
):
    outfile.write(infile.read())

Exception Handling

The __exit__ method receives exception information:

def __exit__(self, exc_type, exc_val, exc_tb):
    if exc_type is not None:
        print(f"Exception occurred: {exc_val}")
    # Return True to suppress the exception
    # Return False (or None) to propagate it
    return False

Suppressing Exceptions

Return True from __exit__ to suppress an exception:

class SuppressError:
    def __enter__(self):
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        return True  # Suppress all exceptions

with SuppressError():
    raise ValueError("This will be suppressed")
# No error raised

Built-in Context Managers

open()

Files are context managers:

with open("file.txt") as f:
    data = f.read()

threading.Lock()

Locks for thread synchronization:

import threading

lock = threading.Lock()

with lock:
    # Only one thread can enter this block
    critical_section()

decimal.localcontext()

Temporarily change decimal precision:

from decimal import Decimal, localcontext

with localcontext() as ctx:
    ctx.prec = 2
    result = Decimal("1.234") * Decimal("5.678")
    print(result)  # 7.0

contextlib.redirect_stdout()

Temporarily redirect standard output:

from contextlib import redirect_stdout
import io

buffer = io.StringIO()

with redirect_stdout(buffer):
    print("This goes to the buffer")

print(buffer.getvalue())

Creating Custom Context Managers

Using a Class

class Timer:
    def __enter__(self):
        self.start = time.time()
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.elapsed = time.time() - self.start
        print(f"Elapsed: {self.elapsed:.2f}s")

with Timer():
    time.sleep(1)
# Elapsed: 1.00s

Using contextlib.contextmanager

A decorator-based approach:

from contextlib import contextmanager
import time

@contextmanager
def timer():
    start = time.time()
    try:
        yield
    finally:
        elapsed = time.time() - start
        print(f"Elapsed: {elapsed:.2f}s")

with timer():
    time.sleep(0.5)
# Elapsed: 0.50s

Using contextlib.AsyncExitStack

For async context managers:

from contextlib import asynccontextmanager

@asynccontextmanager
async def async_timer():
    start = time.time()
    try:
        yield
    finally:
        print(f"Elapsed: {time.time() - start:.2f}s")

Common Patterns

Database Connections

import sqlite3

with sqlite3.connect("database.db") as conn:
    cursor = conn.cursor()
    cursor.execute("SELECT * FROM users")
    results = cursor.fetchall()
# Connection automatically closed

Temporary Files

import tempfile

with tempfile.NamedTemporaryFile(mode="w", delete=False) as tmp:
    tmp.write("Temporary content")
    tmp_name = tmp.name
# File closed, but not deleted (delete=False)

Lock Management

import threading

counter = 0
lock = threading.Lock()

def increment():
    global counter
    with lock:
        counter += 1
        return counter

Best Practices

Always Use with for Resources

# Bad — file may not close on error
f = open("file.txt")
f.write("data")
f.close()

# Good — guaranteed cleanup
with open("file.txt", "w") as f:
    f.write("data")

Avoid Long Operations in with Block

Keep the block short to minimize resource holding time:

# Good — file closed quickly
with open("large_file.txt") as f:
    data = f.read()
process_data(data)  # Outside the with block

Nest Only When Necessary

Deeply nested context managers can be hard to read:

# Hard to read
with open("a") as a, open("b") as b, open("c") as c:
    pass

# Easier to read
with open("a") as a:
    with open("b") as b:
        with open("c") as c:
            pass

See Also