Context manager: Difference between revisions
m (→Python) |
mNo edit summary |
||
(6 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
{{#addbodyclass:tag_tech}} | |||
{{#addbodyclass:tag_prog}} | |||
{{programming}} | |||
Context managers bolt | Context managers are syntactic sugar you use on a code block, to bolt in some implied extra code. | ||
They seem largely used to do resource cleanup more implicitly and automatically, | |||
but there are various other uses. | |||
<!-- | <!-- | ||
Line 34: | Line 39: | ||
At its core, the {{inlinecode|with}} statement takes an object; that object's | At its core, the {{inlinecode|with}} statement takes an object; that object's has a | ||
* __enter__() will be called at the start of the block | * __enter__() that will be called at the start of the block | ||
: and returns the thing that will be assigned | : and returns the thing that will be assigned | ||
* __exit__() will be called afterwards | * __exit__() that will be called afterwards | ||
: also if an exception is raised (so is much like a try-finally) | : also if an exception is raised (so is much like a try-finally) | ||
Line 45: | Line 50: | ||
If | If open() were unaware of this interface, we might add it like in the following example {{comment|(as it is, this 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: | You can write this more manually like: | ||
Line 63: | Line 68: | ||
self.fob.close() | self.fob.close() | ||
# Prove to yourself that it works: | |||
with Test('textfile') as t: | with Test('textfile') as t: | ||
print( t ) | print( t ) | ||
Line 68: | Line 74: | ||
everything before the yield is effectively run as part of __enter__, and that exactly-one yield is what gets assigned | Another way to write something like that is @contextlib.contextmanager decorator. | ||
This is '''further''' 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. | |||
<syntaxhighlight lang="python"> | <syntaxhighlight lang="python"> | ||
Line 83: | Line 92: | ||
print( t ) | print( t ) | ||
</syntaxhighlight> | </syntaxhighlight> | ||
==Javascript== | ==Javascript== |
Revision as of 23:07, 20 April 2024
Context managers are syntactic sugar you use on a code block, to bolt in some implied extra code.
They seem largely used to do resource cleanup more implicitly and automatically,
but there are various other uses.
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()
...because the built-in open() already implements the interface we're about to describe (and e.g. does a close in its __exit__).
At its core, the with statement takes an object; that object's has a
- __enter__() that will be called at the start of the block
- and returns the thing that will be assigned
- __exit__() that will be called afterwards
- also if an exception is raised (so is much like a try-finally)
If open() were unaware of this interface, we might add it like in the following example (as it is, this 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()
# Prove to yourself that it works:
with Test('textfile') as t:
print( t )
Another way to write something like that is @contextlib.contextmanager decorator.
This is further 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 )
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#