Get AI summaries of any video or article — Sign up free
Python Tutorial: Context Managers - Efficiently Managing Resources thumbnail

Python Tutorial: Context Managers - Efficiently Managing Resources

Corey Schafer·
5 min read

Based on Corey Schafer's video on YouTube. If you like this content, support the original creators by watching, liking and subscribing to their content.

TL;DR

Use `with` to tie resource lifetime to a block so teardown runs automatically when the block exits, including on exceptions.

Briefing

Context managers in Python make resource handling reliable by guaranteeing setup and teardown happen automatically—even when errors occur. Instead of manually opening something and remembering to close it later, the `with` statement ties the lifetime of a resource to a block of code, so cleanup runs when the block ends. That reliability matters most for resources like files, database connections, and locks, where forgetting teardown can cause leaks, corrupted state, or lingering locks.

The tutorial first demonstrates the classic file case. It contrasts a manual approach—open a file, write to it, then explicitly close it—with a context-manager approach using `with open(...) as f:`. The key benefit is that the file gets closed properly even if an exception interrupts the block. From there, the focus shifts to building custom context managers so the same automatic cleanup can be applied to user-defined resources.

One custom implementation uses a class. A class-based context manager defines three special methods: `__init__` to store parameters like `file_name` and `mode`, `__enter__` to perform setup (open the file and return the file object), and `__exit__` to perform teardown (close the file). When used like `with OpenFile(sample.txt, 'w') as f:`, the `with` statement calls `__enter__`, binds the returned file object to `f`, runs the block, and then calls `__exit__` to close the file. The tutorial even checks `f.closed` after leaving the block to confirm teardown happened.

A second implementation shows how to write context managers using a function decorated with `contextlib.contextmanager`. Here, the function is a generator: everything before `yield` acts like setup, the `yield` point hands control to the `with` block, and everything after `yield` acts like teardown. The example mirrors the file workflow: open the file before `yield`, yield the file object to the block, and close it after the block finishes. To make teardown robust under exceptions, the tutorial adds a `try`/`finally` structure so cleanup runs even when errors occur.

Finally, the tutorial builds a practical context manager for changing directories. Without a context manager, code typically saves the current working directory, calls `os.chdir(destination)`, performs work, and then switches back—repeated every time. The custom generator-based context manager captures the original directory in setup, changes to the destination, yields control so arbitrary work can run in that directory, and then restores the original directory in teardown. Using `with change_dir(sample_dir_1):` and `with change_dir(sample_dir_2):`, the directory switching becomes clean and reusable, eliminating the need to remember restoration logic.

Overall, the takeaway is that context managers provide a structured pattern for “do setup, run work, always do teardown,” and Python supports both class-based and generator-based implementations to fit different preferences and use cases.

Cornell Notes

Context managers in Python ensure resources are set up and torn down automatically when entering and exiting a `with` block. The tutorial demonstrates this reliability with files: the file is closed even if an exception occurs, removing the need for manual cleanup. It then shows two ways to create custom context managers. A class-based approach uses `__init__`, `__enter__`, and `__exit__` to open and close resources. A generator-based approach uses `@contextmanager`, where code before `yield` is setup and code after `yield` is teardown, typically guarded by `try`/`finally`. The practical example uses a context manager to temporarily change directories and always restore the original working directory.

Why does the `with` statement matter for resource management in Python?

The `with` statement guarantees teardown logic runs when the block ends, not just when execution reaches the end normally. That means cleanup happens even if an exception interrupts the block. In the file example, this prevents forgetting `close()` and avoids leaving files open. The same pattern applies to other resources like database connections and locks, where failing to release can cause leaks or deadlocks.

How does a class-based context manager work internally?

A class-based context manager defines `__init__` to store parameters (like `file_name` and `mode`), `__enter__` to perform setup and return the resource (the opened file), and `__exit__` to perform teardown (closing the file). When used as `with OpenFile(sample.txt, 'w') as f:`, Python calls `__enter__`, binds the returned value to `f`, runs the block, then calls `__exit__` to close the file.

What does `yield` do in a generator-based context manager?

In a function decorated with `@contextmanager`, the code before `yield` runs during setup (e.g., opening a file or saving the current directory). The `yield` statement hands control to the `with` block; if it yields a value (like `yield f`), that value becomes the `as` target inside the block. After the `with` block finishes, execution resumes after `yield`, where teardown happens (like `f.close()` or restoring the original directory).

How can teardown be made exception-safe in a generator-based context manager?

Wrap the setup and the `yield` section in a `try` block and put teardown in a `finally` block. That way, even if an error occurs inside the `with` block, the `finally` section still runs. The tutorial notes that this is the correct pattern for ensuring cleanup under exceptions.

What practical problem does the directory-changing context manager solve?

It removes repetitive boilerplate: saving the current working directory, switching to a destination, doing work, and switching back. The custom context manager captures the original directory with `os.getcwd()`, calls `os.chdir(destination)` for setup, yields control so code can run in the new directory (e.g., `os.listdir()`), and then restores the original directory in teardown. This makes repeated directory operations safer and cleaner.

Review Questions

  1. In a class-based context manager, which methods correspond to setup and teardown, and what does each method return or do?
  2. In a generator-based context manager, what is the role of `yield`, and how does code after `yield` relate to teardown?
  3. How does using `try`/`finally` inside a context manager change behavior when an exception occurs inside the `with` block?

Key Points

  1. 1

    Use `with` to tie resource lifetime to a block so teardown runs automatically when the block exits, including on exceptions.

  2. 2

    A class-based context manager uses `__init__` for parameters, `__enter__` for setup (and returning the resource), and `__exit__` for teardown.

  3. 3

    A generator-based context manager uses `@contextmanager`; code before `yield` is setup, the `with` block runs during the `yield`, and code after `yield` is teardown.

  4. 4

    Guard teardown with `try`/`finally` so cleanup still happens if the block raises an error.

  5. 5

    Custom context managers can replace repetitive manual patterns like open/close and save/restore logic.

  6. 6

    A directory-changing context manager can safely switch directories for a block of work and always restore the original working directory afterward.

  7. 7

    Context managers generalize beyond files to resources like database connections and locks, where reliable cleanup is critical.

Highlights

Context managers eliminate the need to remember manual cleanup by guaranteeing teardown when leaving a `with` block, even under exceptions.
In a class-based context manager, `__enter__` returns the resource (like an opened file) and `__exit__` closes it automatically.
In a generator-based context manager, `yield` marks the boundary between setup and teardown, with teardown running after the `with` block ends.
A practical `change_dir` context manager can replace repeated save/switch/restore directory code with a reusable `with` block.

Topics