Home  >  Article  >  Backend Development  >  How to use Python's with statement

How to use Python's with statement

王林
王林forward
2023-05-25 17:22:062007browse

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!

Statement:
This article is reproduced at:yisu.com. If there is any infringement, please contact admin@php.cn delete