8.5. with 语句

with 语句用于包装带有使用上下文管理器 (参见 with 语句上下文管理器 一节) 定义的方法的代码块的执行。 这允许对普通的 tryexceptfinally 使用模式进行封装以方便地重用。

  1. with_stmt ::= "with" with_item ("," with_item)* ":" suite
  2. with_item ::= expression ["as" target]

带有一个“项目”的 with 语句的执行过程如下:

  1. 对上下文表达式 (在 with_item 中给出的表达式) 求值以获得一个上下文管理器。

  2. 载入上下文管理器的 __enter__() 以便后续使用。

  3. 载入上下文管理器的 __exit__() 以便后续使用。

  4. 发起调用上下文管理器的 __enter__() 方法。

  5. 如果 with 语句中包含一个目标,来自 __enter__() 的返回值将被赋值给它。

    注解

    with 语句会保证如果 __enter__() 方法返回时未发生错误,则 __exit__() 将总是被调用。 因此,如果在对目标列表赋值期间发生错误,则会将其视为在语句体内部发生的错误。 参见下面的第 6 步。

  6. 执行语句体。

  7. 发起调用上下文管理器的 __exit__() 方法。 如果语句体的退出是由异常导致的,则其类型、值和回溯信息将被作为参数传递给 __exit__()。 否则的话,将提供三个 None 参数。

    如果语句体的退出是由异常导致的,并且来自 __exit__() 方法的返回值为假,则该异常会被重新引发。 如果返回值为真,则该异常会被抑制,并会继续执行 with 语句之后的语句。

    如果语句体由于异常以外的任何原因退出,则来自 __exit__() 的返回值会被忽略,并会在该类退出正常的发生位置继续执行。

以下代码:

  1. with EXPRESSION as TARGET:
  2. SUITE

在语义上等价于:

  1. manager = (EXPRESSION)
  2. enter = type(manager).__enter__
  3. exit = type(manager).__exit__
  4. value = enter(manager)
  5. hit_except = False
  6. try:
  7. TARGET = value
  8. SUITE
  9. except:
  10. hit_except = True
  11. if not exit(manager, *sys.exc_info()):
  12. raise
  13. finally:
  14. if not hit_except:
  15. exit(manager, None, None, None)

如果有多个项目,则会视作存在多个 with 语句嵌套来处理多个上下文管理器:

  1. with A() as a, B() as b:
  2. SUITE

在语义上等价于:

  1. with A() as a:
  2. with B() as b:
  3. SUITE

在 3.1 版更改: 支持多个上下文表达式。

参见

PEP 343 - “with” 语句

Python with 语句的规范描述、背景和示例。