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
tryblock is moved to thewithblock finallyblock withclose()call is disappeared: thewithblock closes the file instead after the code inside is executed or exception is raised. The same way astry / finallyworks
The syntax of with Python block:
with context_expression [as context_var]: code_block
context_expressionis 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_varlocal variable accessible inside with block.
Usuallyselfis returned but it could returnNoneas well.- If there is exception on the
context_expressionevaluation or__enter__()call, the code inside and__exit__()are NOT called - If
__enter__()is called successfully, then the code inside thewithblock 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 withTrueis returned, or propagated ifFalseis 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)
