In Python 2.5, the WITH keyword is added. It will use the try ... except ... finally ... The pattern is easily reused. Look at one of the most classic examples:
With open (' file.txt ') as f:
content = F.read ()
In this code, the file will eventually be closed, regardless of what happens during the execution of the code block in the with. If an exception occurs during the execution of the code block, the program closes the file that was opened before the exception is thrown.
Look at another example.
Code like this is often used when initiating a database transaction request:
Db.begin ()
try:
# do some actions
except:
db.rollback ()
raise
finally:
Db.commit ()
If the operation that initiates the transaction request becomes capable of supporting the WITH keyword, then a code like this can be used:
With transaction (db):
# do some actions
The following is a detailed description of the Execute process with, and the above code is implemented in two common ways.
The general execution process of with
A basic with expression, which is structured like this:
Where: EXPR can be any expression; as VAR is optional. The general process of execution is this:
- Evaluates EXPR and gets a context manager.
- The __exit () __ method of the context manager is saved for subsequent calls.
- Invokes the __enter () __ method of the context manager.
- If the with expression contains as Var, then the return value of EXPR is assigned to VAR.
- Executes an expression in the block.
- Invokes the __exit () __ method of the context manager. If an exception occurs during the execution of the block that causes the program to exit, then the exception's type, value, and traceback (that is, the return value of the Sys.exc_info ()) are passed as arguments to the __exit () __ method. Otherwise, three None is passed.
This process is represented in code, as follows:
MGR = (EXPR)
exit = Type (MGR). __exit__ # There is no execution
value = Type (MGR). __enter__ (mgr)
exc = True
try:
try :
var = value # If there is as Var block
except:
exc = False if not
exit (Mgr, *sys.exc_info ()):
raise< C13/>finally:
If exc:
exit (Mgr, none, none, none)
The process has several details:
If any of the methods in the context Manager are not __enter () __ or __exit (), the interpreter throws a attributeerror.
After an exception occurs in the block, if the __exit () __ method returns a value that can be considered True, the exception is not thrown, and the following code continues to execute.
Next, there are two ways to implement the above process.
Implementing the Context Manager class
The first approach is to implement a class that contains the methods required by both the Instance property DB and the context Manager __enter () __ and __exit ().
Class Transaction (object):
def __init__ (self, db):
self.db = DB
def __enter__ (self):
Self.db.begin ()
def __exit__ (self, type, value, Traceback):
If Type is None:
db.commit ()
Else:
db.rollback ()
This implementation is easy to understand after you understand the execution process with. The following is an approach that is much more complicated to understand.
Using the builder Adorner
In the standard library of Python, there is an adorner to get the context manager through the builder. The implementation process using the builder adorner is as follows:
From contextlib import ContextManager
@contextmanager
def transaction (db):
db.begin ()
try:
Yield db
except:
db.rollback ()
raise
Else:
db.commit ()
At first glance, this approach is simpler, but its mechanism is more complex. Take a look at the implementation process:
- After the Python interpreter recognizes the yield keyword, Def creates a generator function that replaces the regular function (I prefer to use a function instead of a method).
- The adorner ContextManager is invoked and returns a Help method that generates a Generatorcontextmanager instance after being invoked. EXPR in the final with expression calls the Help function returned by the Contentmanager adorner.
- The WITH expression calls transaction (db), in effect invoking a help function. The help function calls the Builder function, and the builder function creates a builder.
- The Help function passes this generator to Generatorcontextmanager and creates a Generatorcontextmanager instance object as the context manager.
- The with expression invokes the __enter () _ Method of the context manager of the instance object.
- The next () method of this generator is called in the __enter () __ method. At this point, the builder method stops at yield DB and takes db as the return value for next (). If there is as Var, then it will be assigned to Var.
- The block in the with is executed.
- After block execution ends, the __exit () __ method of the context manager is invoked. The __exit () __ method calls the next () method of the generator again. If a stopiteration exception occurs, pass.
- If the exception builder method does not occur, the Db.commit () is executed, or the Db.rollback () is executed.
Again, the code for the above process is roughly implemented:
def ContextManager (func):
def Helper (*args, **kwargs): Return
Generatorcontextmanager (func (*args, **kwargs) Return
Helper
class Generatorcontextmanager (object):
def __init__ (self, gen):
Self.gen = Gen
def __enter__ (self):
try: Return
self.gen.next ()
except stopiteration:
raise RuntimeError (" Generator didn ' t yield ')
def __exit__ (self, type, value, Traceback):
If Type is None:
try:
Self.gen.next ()
except stopiteration:
pass
else:
raise RuntimeError ("generator didn ' t stop")
else:
try:
self.gen.throw (Type, value, Traceback)
raise RuntimeError ("generator didn" t stop After throw () ")
except stopiteration: Return
True
except:
if Sys.exc_info () [1] isn't value:
Raise
Summarize
Python's with expression contains a lot of Python features. It is worthwhile to take the time to get to the with.
A few other examples
Lock mechanism
@contextmanager
def locked (lock):
lock.acquired ()
try:
yield
finally:
lock.release ()
Standard output redirection
@contextmanager
def stdout_redirect (new_stdout):
old_stdout = sys.stdout
sys.stdout = new_stdout
try:
yield
finally:
sys.stdout = Old_stdout
with open ("File.txt", "W") as F: with
Stdout_redirect (f):
print "Hello World"
Resources
The Python ' with ' Statement by Example
PEP 343