第15章–上下文管理器和else块
15.1 else块
else语句不仅可以用于if后面,也可以用于for while try后
for
仅当 for 循环运行完毕时(即 for 循环没有被 break 语句中止)才运行 else 块。
while
仅当 while 循环因为条件为假值而退出时(即 while 循环没有被 break 语句中止)才运
行 else 块。
try
仅当 try 块中没有异常抛出时才运行 else 块。官方文档(https://docs.python.org/3/
reference/compound_stmts.html)还指出:“else 子句抛出的异常不会由前面的 except 子
句处理。”
15.2 上下文管理器和with块
上下文管理器对象存在的目的是管理with语句
with语句存在的目的是简化try/finally模式
上下文管理器对象是实现了__enter__和__exit__方法的类对象,enter在with语句开始时执行,exit在with语句结束或抛出异常时执行。
as 后的对象是enter返回的值,exit方法的传入参数存储了关于异常的信息,如果with语句中没有抛出异常,那么exit方法中的后三个参数都为None,如果在with语句中抛出了异常,异常的行为按exit方法返回的值是否为True可以分为两种:如果exit方法返回True,则异常不会向上冒泡,异常在with语句内就被处理了;如果exit方法返回不是True,则异常会向上冒泡,具体例子代码如下
无抛出异常,则exit方法的三个参数都为None
class A():
def __enter__(self):
print('A enter')
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print('A exit')
print(exc_type,exc_val,exc_tb)
return True
with A() as a:
print(a)
# A enter
# <__main__.A object at 0x000001D94DE66518>
# A exit
# None None None
with内抛出异常,exit方法返回True,最终程序无报错
class A():
def __enter__(self):
print('A enter')
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print('A exit')
print(exc_type,exc_val,exc_tb)
return True
with A() as a:
print(a)
raise Exception()
# A enter
# <__main__.A object at 0x0000027B689364E0>
# A exit
# <class 'Exception'> <traceback object at 0x0000027B68920788>
#
# Process finished with exit code 0
with内抛出异常,exit方法返回False,最终程序报错,exit code不为0
class B():
def __enter__(self):
print('B enter')
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print('B exit')
print(exc_type,exc_val,exc_tb)
return False
with B() as b:
print(b)
raise Exception()
# B enter
# <__main__.B object at 0x0000021FA308D470>
# B exit
# <class 'Exception'> <traceback object at 0x0000021FA3070C08>
# Traceback (most recent call last):
# File "test.py", line 36, in <module>
# raise Exception()
# Exception
#
# Process finished with exit code 1
15.3 contextlib模块
https://docs.python.org/3/library/contextlib.html
This module provides utilities for common tasks involving the with statement
contextlib是官方的处理with模块相关任务的库
15.4 @contextmanager
@contextmanage可以把一个带yield的生成器函数变为一个上下文管理器
在使用 @contextmanager 装饰的生成器中,yield 语句的作用是把函数的定义体分成两部
分:yield 语句前面的所有代码在 with 块开始时(即解释器调用 __enter__ 方法时)执行,
yield 语句后面的代码在 with 块结束时(即调用 __exit__ 方法时)执行。
如果with语句中抛出了异常,那么异常会传递到contextmanager修饰的生成器中的yield语句,如果在yield语句前后没有try except捕获异常,则程序会中断,导致yield语句后面的语句无法执行,不符合上下文管理器的概念。
所以使用 @contextmanager 装饰器时,要把 yield 语句放在 try/finally 语句中
(或者放在 with 语句中),这是无法避免的,因为我们永远不知道上下文管理器
的用户会在 with 块中做什么
yield不用try except捕获,"after yield"没有被打印出来
from contextlib import contextmanager
@contextmanager
def gen():
print('before yield')
yield 'hello'
print('after yield')
with gen() as f:
print(f)
raise Exception()
# before yield
# hello
# Traceback (most recent call last):
# File "test.py", line 11, in <module>
# raise Exception()
# Exception
#
# Process finished with exit code 1
yield用try except捕获,“after yield”被正常打印
from contextlib import contextmanager
@contextmanager
def gen():
print('before yield')
try:
yield 'hello'
except Exception:
print('catch Exception')
finally:
print('after yield')
with gen() as f:
print(f)
raise Exception()
# before yield
# hello
# catch Exception
# after yield