模块课程(3/4)
上下文管理器:with 和 __enter__/__exit__
上下文管理器是与 with 一起使用的对象。它保证
某些设置/清理操作总是会发生,即使发生错误也是如此。
典型示例:打开和关闭文件。
with 模式
with open("note.txt", "w") as f:
f.write("ciao")
# qui f è già chiuso, anche se write() ha sollevato un'eccezione用简单的英语来说:“以资源 X 打开为 f,执行此操作。完成后
(无论发生什么),关闭 X。”如果没有 with 你就必须写
每次都是 try/finally 。
一次不止一个
with open("a.txt") as f, open("b.txt") as g:
dati_a = f.read()
dati_b = g.read()编写自己的上下文管理器(类)
您需要定义两个 dunder 方法:__enter__ 和 __exit__。
class Timer:
def __enter__(self):
import time
self.t0 = time.perf_counter()
return self # è l'oggetto che finisce dopo "as"
def __exit__(self, exc_type, exc_value, traceback):
import time
self.elapsed = time.perf_counter() - self.t0
# ritornare True ingoia l'eccezione; di solito non lo vuoi
return False
with Timer() as t:
sum(range(10**6))
t.elapsed # quanto è durato__enter__(self)在进入块时被调用;返回值是as之后得到的值。__exit__(self, exc_type, exc_value, tb)在退出时被调用(总是)。如果它返回True,则任何异常都会被抑制。
快捷方式:@contextmanager
contextlib.contextmanager 允许您将上下文管理器编写为
生成器函数:
from contextlib import contextmanager
@contextmanager
def timer():
import time
t0 = time.perf_counter()
try:
yield # qui sta il blocco "with"
finally:
elapsed = time.perf_counter() - t0
print(f"durata: {elapsed:.4f}s")
with timer():
sum(range(10**6))yield 之前的代码是 __enter__,之后的代码是 __exit__。
即使发生错误,try/finally 也能保证清理。
抑制:忽略异常
from contextlib import suppress
with suppress(FileNotFoundError):
open("forse-non-esiste.txt").read()
# se il file non c'è, nessun errore propaga陷阱:with 仅适用于支持它的对象
x = 42
with x: # AttributeError: __enter__
...使用 @contextmanager 编写上下文管理器
标准库的 contextlib 模块导出一个名为 @contextmanager 的非常方便的装饰器,以使用生成器函数创建上下文管理器,而不是编写实现 __enter__ 和 __exit__ 方法的详细类。您只需编写一个使用单个 yield 的生成器函数:
from contextlib import contextmanager
@contextmanager
def manage_resource():
# __enter__ code
yield resource
# __exit__ code自己尝试一下
使用 contextlib.suppress 尝试 int('non-numero') 忽略 ValueError。在块之后分配“ok = True”。评价“好”。
显示提示
抑制(ValueError)仅吞咽ValueError。
3 次尝试后可用的解决方案
复习练习
使用 @contextmanager 编写一个上下文管理器“collect”,它会生成一个空列表,并在退出时将其转换为元组(打印它)。使用它将 1、2、3 附加到块内的列表中。最后,评估“len(collection)”,其中“collection”是生成的列表。
显示提示
产生项目以公开 with 块内的列表。
3 次尝试后可用的解决方案
额外的挑战
Write a custom context manager using the `@contextmanager` decorator from `contextlib`. The context manager must be named `mock_session`. Upon entry, it must append `'start'` to a global list `log_list = []`, yield control using `yield`, and upon exit append `'stop'`. Finally, evaluate `log_list` after using the context manager with `with mock_session():`.
显示提示
在附加“stop”之前,在mock_session中使用yield来暂停执行。
3 次尝试后可用的解决方案