Context manager
Context managers bolt hook in some implied extra code, onto a code blocks, via syntactic sugar.
This is often used for some predicable, always-necessary cleanup, like doing resource management more automatically.
Python
For example:
with open(filename) as file:
file_data = file.read()
is roughly equivalent to
try:
file = open(filename)
file_data = file.read()
finally:
file.close()
At its core, the with statement takes an object; that object's its
- __enter__() will be called at the start of the block
- and returns the thing that will be assignd
- __exit__() will be called afterwards
- also if an exception is raised (so is much like a try-finally)
That example above works as-is because the built-in open() already implements this (and does a close in its __exit__).
(what it is actually equivalent to is down to the implementation of open)
If it didn't do that, we might add it like in the following example (as it is, the example is redundant and pointless. A better example would be a database connection, but it'd take a few more lines to actually be runnable code)
You can write this more manually like:
class Test(object):
def __init__(self, filename): # __init__ isn't part of concept, but is how you get parameters in
self.filename = filename # (without which you'll find it hard to get __enter__ to return something useful to you)
def __enter__(self):
print('__enter__')
self.fob = open(self.filename)
return self.fob
def __exit__(self, exc_type,exc_value, exc_traceback):
print('__exit__')
self.fob.close()
with Test('textfile') as t:
print( t )
You may prefer to use the @contextlib.contextmanager decorator, which is mild trickery where you write a generator,
everything before the yield is effectively run as part of __enter__, and that exactly-one yield is what gets assigned
@contextlib.contextmanager
def test(filename): #
f = open(filename)
try:
yield f
finally:
f.close()
with test('textfile') as t:
print( t )
Notes:
- there is also a way to ease creating a mixin
Javascript
Nothing built in, but because ES6 has generators(good support since ~2017) you can insert both some before and after code, much like that second python example.
To copy-paste from [1]:
function* usingGroup() {
console.group()
try {
yield;
} finally {
console.groupEnd();
}
}
for(const _ of usingGroup()) {
console.log('inside a group')
}
The same page also mentions a library to make this a little easier, imitating python.
C#