Context manager: Difference between revisions

From Helpful
Jump to navigation Jump to search
mNo edit summary
 
(15 intermediate revisions by the same user not shown)
Line 1: Line 1:
{{#addbodyclass:tag_tech}}
{{#addbodyclass:tag_prog}}


{{programming}}


Context managers bolt hook in some implied extra code, onto a code blocks, via syntactic sugar.
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,
Often used to do some resource cleanup more implicitly and automatically, but there are various other uses.  
but there are various other uses.  


<!--
<!--
Line 18: Line 21:




For example:
As things are, e.g.:
<syntaxhighlight lang="python">
<syntaxhighlight lang="python">
with open(filename) as file:
with open(filename) as file:
Line 36: Line 39:




At its core, the {{inlinecode|with}} statement takes an object; that object's its
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 ''functionally'' much like a try-finally, though not as visible)
 
 






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:


If open() didn't already do this {{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)}}, we might add it like:
<syntaxhighlight lang="python">
<syntaxhighlight lang="python">
class Test(object):
class PrintOpen(object):
     def __init__(self, filename):  # __init__ isn't part of concept, but is how you get parameters in
     def __init__(self, filename):   
         self.filename = filename  #  (without which you'll find it hard to get __enter__ to return something useful to you)
         self.filename = filename   
 
   
     def __enter__(self):
     def __enter__(self):
         print('__enter__')
         print('__enter__')
         self.fob = open(self.filename)
         self.fob = open(self.filename)
         return self.fob
         return self.fob
 
   
     def __exit__(self, exc_type,exc_value, exc_traceback):
     def __exit__(self, exc_type,exc_value, exc_traceback):
         print('__exit__')
         print('__exit__')
         self.fob.close()
         self.fob.close()


with Test('textfile') as t:
# Prove to yourself that it works:
with PrintOpen('textfile') as t:
     print( t )
     print( t )
</syntaxhighlight>
</syntaxhighlight>


Notes:
* __init__ isn't part of the concept, but is how you get parameters into an object to use later
: (without which you'll find it hard to get __enter__ to return something useful to you)
* the prints are there to signal when each happens
---
Another (often less readable) way to create almost the same (pointless) example is the @contextlib.contextmanager decorator.


You may prefer to use the @contextlib.contextmanager decorator, which is mild trickery where you write a generator,
This is trickier [[syntax-fu]] (often less readable, but sometimes more succinct), where
everything before the yield is effectively run as part of __enter__, and that exactly-one yield is what gets assigned
* 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 85: Line 98:
     print( t )
     print( t )
</syntaxhighlight>
</syntaxhighlight>
Notes:
* there is also a way to ease creating a [[mixin]]


==Javascript==
==Javascript==

Latest revision as of 10:55, 7 May 2024

Some fragmented programming-related notes, not meant as introduction or tutorial

Data: Numbers in computers ·· Computer dates and times ·· Data structures

Wider abstractions: Programming language typology and glossary · Generics and templating ·· Some abstractions around programming · · Computational complexity theory notes · Synchronous, asynchronous · First-class citizen

Syntaxy abstractions: Constness · Memory aliasing · Binding, assignment, and such · Closures · Context manager · Garbage collection

Sharing stuff: Communicated state and calls · Locking, data versioning, concurrency, and larger-scale computing notes

Language specific: Python notes ·· C and C++ notes · Compiling and linking ·· Lua notes

Teams and products: Programming in teams, working on larger systems, keeping code healthy · Benchmarking, performance testing, load testing, stress testing, etc. · Maintainability

More applied notes: Optimized number crunching · File polling, event notification · Webdev · GUI toolkit notes

Mechanics of duct taping software together: Automation, remote management, configuration management · Build tool notes · Installers


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

This article/section is a stub — some half-sorted notes, not necessarily checked, not necessarily correct. Feel free to ignore, or tell me about it.


As things are, e.g.:

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 functionally much like a try-finally, though not as visible)



If open() didn't already do this (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), we might add it like:

class PrintOpen(object):
    def __init__(self, filename):  
        self.filename = filename   
    
    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 PrintOpen('textfile') as t:
    print( t )

Notes:

  • __init__ isn't part of the concept, but is how you get parameters into an object to use later
(without which you'll find it hard to get __enter__ to return something useful to you)
  • the prints are there to signal when each happens


---

Another (often less readable) way to create almost the same (pointless) example is the @contextlib.contextmanager decorator.

This is trickier syntax-fu (often less readable, but sometimes more succinct), 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

This article/section is a stub — some half-sorted notes, not necessarily checked, not necessarily correct. Feel free to ignore, or tell me about it.

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#

This article/section is a stub — some half-sorted notes, not necessarily checked, not necessarily correct. Feel free to ignore, or tell me about it.