Home  >  Article  >  Backend Development  >  Detailed explanation of using the with keyword in Python

Detailed explanation of using the with keyword in Python

高洛峰
高洛峰Original
2017-03-28 15:21:121590browse

This article mainly introduces the relevant information on the detailed use of the with keyword in Python. In Python, the with keyword is a good thing to manage the context protocol object for you. Friends who need it You can refer to the following

">

In Python 2.5, the with keyword was added. It makes the commonly used try...except...finally... pattern very convenient Reuse. Take a look at the most classic example:

with open('file.txt') as f:
  content = f.read()

In this code, no matter what happens during the execution of the code block in with, the file will eventually be closed if the code block is executed. If an exception occurs during the process, the program will first close the opened file before the exception is thrown.

Look at another example before initiating a database transaction request. When , code like this is often used:

db.begin()
try:
  # do some actions
except:
  db.rollback()
  raise
finally:
  db.commit()

If the operation of initiating a transaction request is changed to support the with keyword, then code like this is enough:

with transaction(db):
  # do some actions

Below, the execution process of with is explained in detail, and the above code is implemented in two common ways.

The general execution process of with

A basic with.

Expression

, its structure is as follows:

with EXPR as VAR:
  BLOCK
Among them: EXPR can be any expression; as VAR is optional. Its general execution process is as follows:

    Calculate EXPR and obtain a context manager.
  1. ##The exit() method of the context manager is saved for subsequent calls ##.
  2. #Call the enter() method of the context manager.

  3. If the with expression contains as VAR, the return value of EXPR is assigned to VAR.

  4. ##Execute the expression in BLOCK

  5. ##Call the exit() method of the context manager if an error occurs during the execution of BLOCK. If the exception causes the program to exit, the exception's type, value and traceback (that is, the return value of sys.exc_info()) will be passed to the exit() method. Otherwise, three None will be passed.
  6. #Represent this process in code, as follows:

    mgr = (EXPR)
    exit = type(mgr).exit # 这里没有执行
    value = type(mgr).enter(mgr)
    exc = True
    try:
      try:
        VAR = value # 如果有 as VAR
        BLOCK
      except:
        exc = False
        if not exit(mgr, *sys.exc_info()):
          raise
    finally:
      if exc:
        exit(mgr, None, None, None)
    This process has several details:
  7. If there is no enter() or exit() in the context manager Either method, then the interpreter will throw an AttributeError.

    After an exception occurs in BLOCK, if the exit() method returns a value that can be regarded as True, then the exception will not be thrown, and the subsequent code will continue to execute.

  8. Next, use two methods to implement the above process.

Implementing the context manager class

The first method is to implement a class that contains an instance

Attributes

db and the methods required by 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()
After understanding the execution process of with, this implementation is easy to understand. The implementation method introduced below is much more complicated to understand.

Using

Generators

Decorators

In Python's standard library, there is a decorator that can get the context manager through the generator. The implementation process using the generator decorator 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 implementation is simpler, but its mechanism is more complex. Take a look at its execution process:

After the Python interpreter recognizes the yield keyword, def will create a generator Function

Replace the regular function (in the class definition I prefer to use functions instead of methods).

The decorator contextmanager is called and returns a helper method, which will generate a GeneratorContextManager instance after being called. Finally, the EXPR in the with expression calls the helper function returned by the contentmanager decorator.
  1. with expression calls transaction(db), which actually calls the helper function. The helper function calls the generator function, which creates a generator.

  2. The helper function passes this generator to GeneratorContextManager and creates an instance object of GeneratorContextManager as the context manager.

  3. with 表达式调用实例对象的上下文管理器的 enter() 方法。

  4. enter() 方法中会调用这个生成器的 next() 方法。这时候,生成器方法会执行到 yield db 处停止,并将 db 作为 next() 的返回值。如果有 as VAR ,那么它将会被赋值给 VAR 。

  5. with 中的 BLOCK 被执行。

  6. BLOCK 执行结束后,调用上下文管理器的 exit() 方法。 exit() 方法会再次调用生成器的 next() 方法。如果发生 StopIteration 异常,则 pass 。

  7. 如果没有发生异常生成器方法将会执行 db.commit() ,否则会执行 db.rollback() 。

再次看看上述过程的代码大致实现:

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] is not value:
          raise

总结

Python的 with 表达式包含了很多Python特性。花点时间吃透 with 是一件非常值得的事情。

一些其他的例子

锁机制

@contextmanager
def locked(lock):
  lock.acquired()
  try:
    yield
  finally:
    lock.release()

标准输出重定向

@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"

The above is the detailed content of Detailed explanation of using the with keyword in Python. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn