我正在尝试编写一个上下文管理器函数。它“有效”,但我注意到即使在“with”退出后,上下文中的
__repr__
方法也会继续触发。
这是 TimedContext 类:
import time
class TimedContext:
def __init__(self):
self.start = self.now = self.duration = 0
def __enter__(self):
self.start = time.time()
return self
def __repr__(self):
self._update_duration()
return f"{self.duration}"
def __exit__(self, type_, value, traceback):
self._update_duration()
return traceback is None
def _update_duration(self):
self.now = time.time()
self.duration = self.now - self.start
这是我使用它:
from TimedContext import TimedContext
import time
with TimedContext() as t:
time.sleep(1)
print(f"Time0: {t}")
print(f"Time1: {t}")
time.sleep(1)
print(f"Time2: {t}")
这是我运行脚本的输出:
Time0: 1.001316785812378
Time1: 1.0014879703521729
Time2: 2.0025713443756104
我预计 Time2 与 Time1 相同,因为我预计
with
的结尾完成了上下文,因此 t
现在只是一个包含上下文最终返回的变量。 (我的措辞在这里吗?)
我更新
__repr__
函数中的持续时间的原因是我希望能够从“with”内部和外部引用 t
。
我可以通过创建一个布尔值
exit_called
,将其设置为False(在__init__
上)和True(在__exit__
上设置为True)来解决此“问题”,然后仅在self.duration
为False时更新exit_called
。虽然这个“有效”,但我预计上下文会消失,因为 with
已完成并且 __exit__
已运行完成。然而,上下文似乎仍然存在。它是确定性的吗?上下文什么时候被清理?我以为 with
的关闭就是这么做的。我需要做些什么来清理上下文,还是需要明确地执行此操作?
我想了解这里应该发生什么。我没想到一旦
__exit__
完成,上下文就会保留下来。也许我应该以不同的方式解决我的问题?
来自解开
with
声明:
with a as b: body...
相当于
_enter = type(a).__enter__ _exit = type(a).__exit__ b = _enter(a) try: body... except: if not _exit(a, *sys.exc_info()): raise else: _exit(a, None, None, None)
with
本身不进行任何清理,它调用 __exit__()
方法来执行此操作。它也不会删除 b
变量——它仍然包含由 as b
分配的值。
例如,当您使用
with open(filename) as f:
code...
print(f)
f
仍将包含文件对象,但它将被关闭且无法使用,因为其 __exit__()
方法会关闭文件并回收所有资源。
这与分配在块中使用的变量的其他结构没有什么不同。例如,
for
语句中的变量可以在循环完成后使用——它将包含上次迭代的值。
当变量超出范围时,上下文管理器对象本身最终将被垃圾收集器清理。