Python 上下文管理器中的 __exit__ 需要什么? [已关闭]

问题描述 投票:0回答:1

我正在尝试编写一个上下文管理器函数。它“有效”,但我注意到即使在“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__
完成,上下文就会保留下来。也许我应该以不同的方式解决我的问题?

python contextmanager
1个回答
2
投票

来自解开

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
语句中的变量可以在循环完成后使用——它将包含上次迭代的值。

当变量超出范围时,上下文管理器对象本身最终将被垃圾收集器清理。

© www.soinside.com 2019 - 2024. All rights reserved.