updated 30 Dec, 2021
Python context manager: try / finally in better way
Limited system resources: don't forget to free them.
When we work with the system resources, we should release them in the end, because the number of currently opened files or network connections is limited by OS or by the process that provides them f.e. database connections. Let's close the file after reading its contents:
f = open("notes.txt") lines = f.readlines() print(lines) f.close()Even better to do this in safer way, to guarantee the closing in case of the errors before the
close()
is called:
f = open("notes.txt") try: lines = f.readlines() print(lines) finally: f.close()There are lot of code there. Let's do this in shorter way:
with open("notes.txt") as f: lines = f.readlines() print(lines)What happens above:
- Open the file and create the variable, the same as
f = open("notes.txt")
- The code inside the
try
block is moved to thewith
block finally
block withclose()
call is disappeared: thewith
block closes the file instead after the code inside is executed or exception is raised. The same way astry / finally
works
The syntax of with
Python block:
with context_expression [as context_var]: code_block
context_expression
is evaluated to an object that the implements the context manager protocol: the class that contains the__enter__()
,__exit__()
functions__enter__()
is called and the returned object assigned to thecontext_var
local variable accessible inside with block.
Usuallyself
is returned but it could returnNone
as well.- If there is exception on the
context_expression
evaluation or__enter__()
call, the code inside and__exit__()
are NOT called - If
__enter__()
is called successfully, then the code inside thewith
block is called and__exit__()
always called, even if an exception is raised - If an exception is raised then it's passed into the
__exit__()
call as argument and it could be supressed withTrue
is returned, or propagated ifFalse
is returned
class FileContextManager(object): def __init__(self, file_name, method): self.file = open(file_name, method) # get resource: open file def __enter__(self): return self.file # return file object def __exit__(self, type, value, traceback): self.file.close() # release resource: close file
When is it useful?
Working with files, network and database connections i.e. any limited system resource that is acquired then released after.Can I implement own context manager without a class?
Yes, it could be done with generator function like this:from contextlib import contextmanager @contextmanager def my_open_file(name): f = open(name, 'w') try: yield f # run the code inside with block finally: f.close() with my_open_file("some.txt") as f: f.readlines()The library
GeneratorContextManager
class is used there internally.
Or use closing()
decorator, if the resource provides close()
method for release:
from contextlib import closing from urllib.request import urlopen with closing(urlopen('https://www.python.org')) as lines: for line in lines: print(line)