Context manager: Difference between revisions

From Helpful
Jump to navigation Jump to search
 
(8 intermediate revisions by the same user not shown)
Line 21: 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 44: Line 44:


* __exit__() that 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)




Line 50: Line 50:




If open() didn't already do this, 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)}}
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:
 
You can write this more manually like:
 
<syntaxhighlight lang="python">
<syntaxhighlight lang="python">
class Test(object):
class PrintOpen(object):
     def __init__(self, filename):   
     def __init__(self, filename):   
         self.filename = filename   
         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__')
Line 69: Line 66:


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


Notes:  
Notes:  
* __init__ isn't part of the concept, but is how you get parameters in
* __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)
: (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
* 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.
Another (often less readable) way to create almost the same (pointless) example is the @contextlib.contextmanager decorator.


This is '''further''' trickery where
This is trickier [[syntax-fu]] (often less readable, but sometimes more succinct), where
* you write a generator,  
* you write a generator,
* everything before the yield is effectively run as part of __enter__, and  
* everything before the yield is effectively run as part of __enter__, and  
* that exactly-one yield is what gets assigned.
* that exactly-one yield is what gets assigned.

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.