Statement body (with-body): The code block wrapped in the with statement will call the enter() method of the context manager before executing the statement body, and the exit() method will be executed after the statement body is executed.
Basic syntax and working principle
The syntax format of the with statement is as follows:
Listing 1. Syntax format of the with statement
with context_expression [as target(s)]:
with-body
Here contextexpression returns a context manager object, which is not assigned to the as clause In target(s), if the as clause is specified, the return value of the context manager's _enter() method will be assigned to target(s). target(s) can be a single variable, or a tuple enclosed by "()" (it cannot be a list of variables separated only by ",", "()" must be added).
Python has improved some built-in objects and added support for context managers, which can be used in with statements, such as automatically closing files, automatically acquiring and releasing thread locks, etc. Suppose you want to operate a file, you can use the with statement to have the following code:
Listing 2. Use the with statement to operate the file object
with open(r'somefileName' ) as somefile:
for line in somefile:
print line
# ...more code
The with statement is used here, no matter what is being processed Whether an exception occurs during the file process, it can be ensured that the open file handle has been closed after the with statement is executed. If you use the traditional try/finally paradigm, you need to use code similar to the following:
Listing 3. Try/finally mode to operate file objects
somefile = open(r' somefileName')
try:
for line in somefile:
print line
# ...more code
finally:
somefile.close()
In comparison, using the with statement can reduce the amount of coding. Modules threading, decimal, etc. have also been added to support the context management protocol.
PEP 0343 describes the implementation of the with statement. The execution process of the with statement is similar to the following code block:
Listing 4. The execution process of the with statement
context_manager = context_expression
exit = type(context_manager) .__exit__
Value = type(context_manager).__enter__(context_manager)
exc = True # True means normal execution, even if there is an exception, it will be ignored; False means re-throwing the exception, and the exception needs to be treated Process
try:
try:
target = value # If the as clause is used
with-body #Execute with-body
except: # An exception occurs during execution
exc = False
# If __exit__ returns True, the exception is ignored; if it returns False, the exception is re-thrown
#Exception handling by outer code
if not exit(context_manager, *sys.exc_info()):
raise
finally:
#Exit normally, or exit through the break/continue/return statement in the statement-body
#Or ignore the exception and exit
if exc:
exit (context_manager, None, None, None)
# Returns None by default, None is regarded as False in a Boolean context
Execute context_expression to generate the context manager context_manager.
Call the enter() method of the context manager; if the as clause is used, assign the return value of the enter() method to the target(s) in the as clause.
Execution statement body with-body
Regardless of whether an exception occurs during execution, the exit() method of the execution context manager is executed. The exit() method is responsible for execution. "Cleaning" work, such as releasing resources, etc. If no exception occurs during execution, or the statement break/continue/return is executed in the statement body, call exit(None, None, None) with None as the parameter; if an exception occurs during execution, use sys.excinfo The exception information is the parameter call _exit(exc_type, exc_value, exc_traceback).
When an exception occurs, if exit(type, value, traceback) returns False, the exception will be re-thrown and the statement logic other than with will be used to handle the exception. This is also a common practice; if it returns True, then Ignore the exception and no longer handle the exception.
Custom context manager
Developers can customize classes that support the context management protocol. The custom context manager needs to implement the enter() and exit() methods required by the context management protocol:
Contextmanager._enter(): Enter the runtime context of the context manager and execute it in the statement body Called before. The with statement assigns the method's return value to the target in the as clause, if one is specified.
Contextmanager._exit(exc_type, exc_value, exc_traceback): Exits the runtime context related to the context manager and returns a Boolean value indicating whether to handle the exception that occurred. The parameters indicate the exception that caused the exit operation. If no exception occurs during exit, all three parameters are None. If an exception occurs, return.
True means not to handle the exception, otherwise the exception will be re-thrown after exiting the method to be handled by code logic outside the with statement. If an exception is raised within this method, it will replace the exception raised by the statement in statement-body. When handling an exception, do not explicitly rethrow the exception, that is, you cannot rethrow the exception passed in through the parameters. You only need to set the return value to False. The context management code then detects whether exit() failed to handle the exception.
The following is a simple example to demonstrate how to build a custom context manager. Note that the context manager must provide definitions for both the enter() and exit() methods, the absence of either will result in an AttributeError; the with statement first checks whether the exit() method is provided, and then checks whether the enter() method is defined.
Suppose there is a resource DummyResource. This resource needs to be allocated before accessing and released after use. The allocation operation can be placed in the enter() method, and the release operation can be placed in the exit() method. . For the sake of simplicity, only print statements are used to indicate the current operation, and there is no actual resource allocation and release.
Listing 5. Custom objects that support the with statement
class DummyResource:
def __init__(self, tag):
Self.tag = tag
print 'Resource [%s]' % tag
def __enter__(self):
print '[Enter %s]: Allocate resource .' % self.tag
Return self # Different objects can be returned
def __exit__(self, exc_type, exc_value, exc_tb):
print '[Exit %s ]: Free resource.' % self.tag
if exc_tb is None:
print '[Exit %s]: Exited without exception.' % self.tag
else:
print '[Exit %s]: Exited with exception raised.' % self.tag
return False # Can be omitted, the default None is also regarded as False
Enter() in DummyResource returns a reference to itself, which can be assigned to the target variable in the as clause; the type of the return value can be set to a different type according to actual needs, and does not have to be a context manager object. itself.
The variable exctb is detected in the exit() method. If it is not None, it means that an exception has occurred. Returning False means that the exception needs to be handled by external code logic; note that if no exception occurs, the default The return value is None, which is also regarded as False in a Boolean environment. However, since no exception occurs, the three parameters of _exit() are None. The context management code can detect this situation and handle it normally.
The following is to access DummyResource in the with statement:
Listing 6. Using a custom object that supports the with statement
with DummyResource('Normal' ):
print '[with-body] Run without exceptions.'
with DummyResource('With-Exception'):
print '[with-body] Run with exception.'
raise Exception
print '[with-body] Run with exception. Failed to finish statement-body!'
Execution of the first with statement The results are as follows:
Listing 7. With statement 1 execution result
Resource [Normal]
[Enter Normal]: Allocate resource.
[with-body] Run without exceptions.
[Exit Normal]: Free resource.
[Exit Normal]: Exited without exception.
You can see , during normal execution, the statement body with-body will be executed first, and then the exit() method will be executed to release resources.
The execution result of the second with statement is as follows:
Listing 8. The execution result of with statement 2
Resource [With-Exception]
[Enter With-Exception]: Allocate resource.
[with-body] Run with exception.
[Exit With-Exception]: Free resource.
[Exit With-Exception]: Exited with exception raised.
Traceback (most recent call last):
File "G:/demo", line 20, in raise Exception
Exception
It can be seen that when an exception occurs in with-body, with-body has not been executed, but the resources are guaranteed to be released. At the same time, the exception generated is caused by the code logic outside the with statement. capture processing.
You can customize the context manager to manage resources in the software system, such as database connections, access control of shared resources, etc. The Python online documentation Writing Context Managers provides a simple example of a context manager for managing database connections.
Contextlib module
The contextlib module provides three objects: the decorator contextmanager, the function nested and the context manager closing. Using these objects, you can wrap existing generator functions or objects and add support for the context management protocol, avoiding the need to write a context manager specifically to support the with statement.
Decorator contextmanager
Contextmanager is used to decorate the generator function. After the generator function is decorated, a context manager is returned, and its enter() and exit() methods are The contextmanager is responsible for providing it instead of the previous iterator. The decorated generator function can only produce one value, otherwise it will cause an exception RuntimeError; the generated value will be assigned to the target in the as clause, if the as clause is used. Let's look at a simple example.
List 9. Decorator contextmanager usage example
from contextlib import contextmanager
@contextmanager
def demo():
print '[Allocate resources]'
print 'Code before yield-statement executes in __enter__'
yield '*** contextmanager demo ***'
print 'Code after yield-statement executes in __exit__'
print '[Free resources]'
with demo() as value:
print 'Assigned Value: %s' % value
The result output is as follows:
Listing 10. Contextmanager usage example execution result
[Allocate resources]
Code before yield-statement executes in __enter__
Assigned Value: *** contextmanager demo ***
Code after yield-statement executes in __exit__
[Free resources]
It can be seen that the statement before yield in the generator function is executed in the enter() method, the statement after yield is executed in exit(), and the value generated by yield is assigned to the as clause. value variable.
It should be noted that the contextmanager only omits the writing of enter() / exit(), but is not responsible for the "acquisition" and "cleaning" of resources; the "acquisition" operation needs to be defined in the yield statement Previously, the "cleaning" operation needed to define the yield statement, so that the with statement would execute these statements to obtain/release resources when executing the enter() / exit() method, that is, the necessary logic control, including resources, needed to be implemented in the generator function Throw appropriate exceptions when access errors occur.
Function nested
Nested can organize multiple context managers together to avoid using nested with statements.
Listing 11. nested syntax
with nested(A(), B(), C()) as (X, Y, Z):
#with-body code here
Similar to:
Listing 12. nested execution process
with A() as X:
with B() as Y:
with C() as Z:
# with-body code here
It should be noted that an exception occurs Finally, if a context manager's exit() method returns False for exception handling, the outer context manager will not detect the exception.
Context manager closing
The implementation of closing is as follows:
Listing 13. Context management closing implementation
class closing(object):
# help doc here
def __init__(self, thing):
self.thing = thing
def __enter__(self):
return self.thing
def __exit__(self, *exc_info):
self.thing.close()
Context The manager will assign the wrapped object to the target variable of the as clause, and ensure that the opened object will be closed after with-body is executed. The object wrapped by the closing context manager must provide the definition of the close() method, otherwise an AttributeError will be reported during execution.
Listing 14. Custom objects that support closing
class ClosingDemo(object):
def __init__(self):
self.acquire()
def acquire(self):
print 'Acquire resources.'
def free(self):
print ' Clean up any resources acquired.'
def close(self):
self.free()
with closing(ClosingDemo()):
print 'Using resources'
The result output is as follows:
Listing 15. Output result of custom closing object
Acquire resources.
Using resources
Clean up any resources acquired.
Closing is applicable to objects that provide close() implementation, such as network connections, database connections, etc. It can also be passed when customizing classes. Interface close() to perform the required resource "cleaning" work.
The above is the detailed content of How to use Python's with statement. For more information, please follow other related articles on the PHP Chinese website!