First, Introduction
With is a new syntax introduced from Python 2.5, more precisely, a contextual management protocol that simplifies the process of try...except...finally. With is initialized by the __enter__ method, and then the __exit__ is done and the exception is handled. With some tasks that need to be pre-set and cleaned up afterwards, with provides a very handy expression.
The basic syntax for with is as follows, expr is an arbitrary expression, VAR is a single variable (can be a tuple), and "as VAR" is optional.
Copy the Code code as follows:
With EXPR as VAR:
BLOCK
According to Pep 343 's explanation, with...as ... will be translated into the following statement:
Copy CodeThe code is as follows:
MGR = (EXPR)
Exit = Type (MGR). __exit__ # not calling it yet
Value = Type (MGR). __enter__ (MGR)
EXC = True
Try
Try
var = value # only if "as VAR" is present
BLOCK
Except
# The exceptional case was handled here
EXC = False
If not exit (Mgr, *sys.exc_info ()):
Raise
# The exception is swallowed if Exit () returns True
Finally
# The normal and Non-local-goto cases is handled here
If exc:
Exit (Mgr, none, none, none)
Why is it so complicated? Note that the code in the finally does not perform a finally cleanup until the block is executed, because when expr executes, an exception is thrown, and access to Mgr.exit execution will report attributeerror error.
Ii. means of realization
As you can see from the front facing with translation, the object that is evaluated by with must have a __enter__ method and a __exit__ method. Take a look at a file read example, notice here we have to solve 2 problems: File read exception, after reading, close the file handle. This is usually written in try...except:
Copy the Code code as follows:
f = open ('/tmp/tmp.txt ')
Try
For line in F.readlines ():
Print (line)
Finally
F.close ()
Note that we do not have to deal with file open failed IOError, the above wording can work properly, but for each open file, we have to manually close the file handle. If you want to use with to accomplish these functions, you need a proxy class:
Copy CodeThe code is as follows:
Class opened (object):
def __init__ (self, name):
Self.handle = open (name)
def __enter__ (self):
Return Self.handle
def __exit__ (self, type, value, Trackback):
Self.handle.close ()
With opened ('/tmp/a.txt ') as F:
For line in F.readlines ():
Print (line)
Note that we have a name called opened Auxiliary class, and implemented the __enter__ and __exit__ methods, __enter__ method has no parameters, the __exit__ method of 3 parameters, respectively, represents the type of exception, value, and stack information, if there is no exception, The value of 3 entry parameters is none.
If you don't like the definition of class, you can also use the contextlib provided by the Python standard library to implement:
Copy the Code code as follows:
From Contextlib import ContextManager
@contextmanager
Def opened (name):
f = Open (name)
Try
Yield F
Finally
F.close ()
With opened ('/tmp/a.txt ') as F:
For line in F.readlines ():
Print (line)
With the ContextManager function, yield can only return one parameter, and yield is followed by code that handles cleanup work. In our example of reading a file, we close the file handle. Here the principle is the same as the class opened we implemented before, and interested can refer to the source code of ContextManager.
Third, the application scenario
Nonsense so much, so in the end those scenes should use with, there are some excellent examples? Of course, otherwise what is the meaning of this article? The following excerpt from PEP 343.
A template that ensures that the code is unlocked before it executes and releases the lock after execution:
Copy the Code code as follows:
@contextmanager
def locked (lock):
Lock.acquire ()
Try
Yield
Finally
Lock.release ()
With locked (myLock):
# Code here executes with MyLock held. The lock is
# guaranteed to being released when the block was left (even
# if Via return or by an uncaught exception).
Commit and rollback of database transactions:
Copy CodeThe code is as follows:
@contextmanager
def transaction (DB):
Db.begin ()
Try
Yield None
Except
Db.rollback ()
Raise
Else
Db.commit ()
REDIRECT stdout:
Copy CodeThe code is as follows:
@contextmanager
def stdout_redirected (new_stdout):
Save_stdout = Sys.stdout
Sys.stdout = New_stdout
Try
Yield None
Finally
Sys.stdout = Save_stdout
With opened (filename, "w") as F:
With stdout_redirected (f):
print "Hello World"
Note that the above example is not thread-safe and should be used with caution in a multi-threaded environment.
Iv. Summary
With is a simplification of the try...expect...finally syntax and provides a very good way to handle exceptions. There are 2 ways to implement the WITH syntax in Python: class-based and decorator-based,2 are equivalent in principle and can be selected according to the specific scenario.
With originally originated from a kind of block...as ... syntax, but this syntax has been spurned by many, and was finally born with, and it is still possible to refer to PEP-343 and PEP-340 for this period of history.