We’ve learned that we can create our own context managers using the class-based method, but there’s an even simpler way of creating context managers. We can use a built-in Python module called contextlib
!
The contextlib
module allows for the creation of a context manager with the use of a generator function (a function that uses yield
instead of return
) and the contextlib decorator - @contextmanager
. Instead of creating a class and definining __enter__
and __exit__
methods, we can use a simple function!
There are a few steps in the setup so let’s take it slow and break down each step. First, we will need to import the built-in module into our script and grab the @contextmanager
decorator:
from contextlib import contextmanager
Once we have successfully imported the module, we can automatically use the @contextmanager
decorator to wrap a simple generator function:
from contextlib import contextmanager @contextmanager def open_file_contextlib(file, mode): opened_file = open(file, mode) try: yield opened_file finally: opened_file.close()
We are doing a few things here:
- We have written a generator function called
open_file_contextlib
with the expectation that it will take in two arguments, a file and a mode. - We then use the built-in
open()
function to open the file (that we received as an argument) and save it to a variable calledopened_file
. - The function then will attempt (via a
try
statement) toyield
the opened file and complete whatever code we pass when we use it in conjunction with thewith
statement. More on this in a bit! - Lastly the resource (file) will be closed once all the code is done being executed.
If we think about this structure in sections relative to the class-based approach, it essentially breaks down into this:
@contextmanager def generator_function(<parameters>): <setup section - equivalent to __enter__ > try: yield <value> finally: <cleanup section - equivalent to __exit__ >
Once we have created this function and denoted it as a context manager using the @contextmanager
decorator, we can immediately use it like before in a with
statement:
with open_file_contextlib('file.txt', 'w') as opened_file: opened_file.write('We just made a context manager using contexlib')
Following this pattern of creating context managers allows us to quickly convert generator functions to become context managers without the need to create any extra classes. Now, let’s remake our poem context manager following this pattern!
Instructions
Let’s create our PoemFiles
context manager from previous exercises. First, import contextmanager
from contextlib
.
Now, let’s create a generator function called poem_files
that has two parameters file
and mode
. The function should do two things:
- Print
'Opening File'
- Open the file using
open()
with thefile
andmode
parameters, and save the result to a variable calledopen_poem_file
.
Don’t forget to decorate it with the @contextmanager
decorator.
Next, we will have to create the try
/finally
structure. Inside of the function write the try
clause, and inside of it use the yield
keyword to yield the open_poem_file
variable.
Now, let’s finish the try
/finally
block by writing a finally
clause that does two things:
- Print
'Closing File'
- Call
close()
on theopen_poem_file
variable.
Uncomment and run the with
statement below your script.