Context manager and with block as follows
The context manager object exists to manage with statements, just as iterators exist to manage for statements.
The purpose of the with statement is to simplify the try/finally pattern. This pattern is used to ensure that a piece of code performs an action when it finishes running, even if that piece of code aborts due to an exception, return statement, or () call. The code in the finally clause is often used to release important resources or to restore temporarily changed state.
== The context manager protocol contains the enter and exit methods ==. The enter method is called on the context manager object when the with statement begins to run. When the with statement finishes running, the exit method is called on the context manager object, playing the role of the finally clause.
== The result of executing the expression following with is the context manager object, and binding the value to the target variable (the as clause) is the result of calling the enter method on the context manager object == The as clause of the with statement is optional. For the open function, the as clause must be added in order to get a reference to the file. However, some context managers will return None because there is nothing useful to offer the user in terms of objects.
with open('') as fp: ...
Customized context classes:
class A: def __init__(self, name): = name def __enter__(self): print('enter') return def __exit__(self, exc_type, exc_val, exc_tb): print('gone') with A('xiaozhe') as dt: print(dt)
contextlib module
There are also classes and other functions in the contextlib module that are more widely used.
closing: If an object provides the close() method but does not implement theenter/exitprotocol, then the context manager can be constructed using this function.
suppress: builds a context manager that temporarily ignores the specified exception.
@contextmanager:== This decorator turns simple generator functions into context managers== so that classes don't have to be created to implement the manager protocol.
ContextDecorator: this is a base class for defining class-based context managers. This context manager can also be used to decorate functions, running the entire function in a managed context
ExitStack: This context manager has access to multiple context managers. At the end of the with block, ExitStack calls each context manager on the stack in the order of last-in-first-out.exitMethods.
The most widespread use of == is the @contextmanager decorator, so be extra careful. This decorator also has a confusing side, as it has nothing to do with iteration but uses a yield statement==.
Using @contextmanager
The @contextmanager decorator reduces the amount of sample code needed to create a context manager. Instead of writing a complete class defining the enter and exit methods, all that is needed is to implement a generator with a yield statement that generates the value you want the enter method to return.
In a generator decorated with @contextmanager, the yield statement serves to split the body of the function definition into two parts: all code preceding the ==yield statement is executed at the beginning of the with block (when the interpreter calls the enter method), and the code following the yield statement is executed at the end of the with block (when the exit method is called)= ==. ==.
import contextlib @ def test(name): print('start') yield name print('end') with test('zhexiao123') as dt: print(dt) print('doing something')
Implementation Principle
The decorator wraps the function into a class that implements the enter and exit methods. The name of the class is _GeneratorContextManager.
The enter method of this class does the following.
1. Call the generator function to save the generator object (called gen here).
2. Call next(gen), which executes to the location of the yield keyword.
3. Return the value produced by next(gen) to bind the output to the target variable in the with/as statement.
The exit method does several things when the with block terminates:
1. Check that no exceptions have been passed to exc_type; if so, call (exception), which throws the exception on the line containing the yield keyword in the body of the generator function definition.
2. Otherwise, call next(gen) to continue executing the code after the yield statement in the generator function definition body.
Exception handling
To tell the interpreter that the exception has been handled, the exit method returns True, at which point the interpreter suppresses the exception. If the exit method does not explicitly return a value, then the interpreter gets None and then bubbles up the exception.
When using the @contextmanager decorator, the default behavior is the opposite: the exit method provided by the decorator assumes that all exceptions sent to the generator are handled, and therefore should be suppressed. If you do not want @contextmanager to suppress exceptions, you must explicitly rethrow them in the decorated function.
There's a bug in the code above: if an exception is thrown in the with block, the Python interpreter catches it and then throws it again in the test function's yield expression. But there's no code there to handle the error, so the test function aborts.
When using the @contextmanager decorator, put the yield statement in the try/finally statement, because we never know what the user of the context manager will do in the with block.
import contextlib @ def test(name): print('start') try: yield name except: raise ValueError('error') finally: print('end') with test('zhexiao123') as dt: print(dt) print('doing something')
This is the whole content of this article.