对系统资源使用上下文管理器有错吗?

Is it ever wrong to use a context manager with system resources?

试图在更深层次上理解事物。

如果我打开一个文件,或者一个网络请求,tensorflow 会话,或者任何可以用 with 语句处理的东西;有没有 曾经 我不应该使用 with 语句的时候?

例如,有没有什么时候我应该使用更通用/通用的 try except 结构?

我真正的问题是,with 真正作用的底层结构是什么?我读了一些不错的 helpful hints as well as the documentation itself,但 with 的一些内部工作原理对我来说仍然有点像黑魔法。我正在尝试揭开神奇成分的神秘面纱。

我总是去 Python 增强建议 (PEP) 来理解 python 中的概念,因为与文档相比,它们更侧重于事物的概念推理,并且通常直接解决:

  • 新功能/更改的原因。
  • 如何使用现有代码完成/它如何影响现有代码。

因为您对实现方面感兴趣,这里是 PEP 343 - the "with" statement 的相关成果:

Specification: The 'with' Statement

A new statement is proposed with the syntax:

with EXPR as VAR:
    BLOCK

(paragraph omitted - not really relevent for this question)

The translation of the above statement is:

mgr = (EXPR)
exit = type(mgr).__exit__  # Not calling it yet
value = type(mgr).__enter__(mgr)
exc = True
try:
    try:
        VAR = value  # Only if "as VAR" is present
        BLOCK
    except:
        # The exceptional case is handled here
        exc = False
        if not exit(mgr, *sys.exc_info()):
            raise
        # The exception is swallowed if exit() returns true
finally:
    # The normal and non-local-goto cases are handled here
    if exc:
        exit(mgr, None, None, None)

因此 with 语句的内部工作原理与 try: finally 构造完全相同,只是语法更清晰,更难忘记关闭文件等。

不使用 with 语句的原因如下:如果 failure/success 需要自定义清理。

通常您不需要知道在处理完文件后应该执行哪些清理。 with 语句无论如何都会关闭文件(除了 Python 没有机会进入上下文管理器的 __exit__ 方法的情况,例如系统突然关闭或类似的非常特别的东西)。

但是,如果您需要执行一些 localimportant 清理,那么使用 try/except/finally 可能 更有意义。此上下文中的一个重要关键字是:separation of concerns.

假设您调用一个函数,该函数假设创建了一个不受 Pythons GC 管理的对象,并打开一个文件并尝试将其写入文件。在这种情况下,您需要执行比打开和关闭文件更多的清理工作:

def func():
    bad_object = create_object_that_cannot_be_cleaned_by_pythons_gc():
    try:
        with open(filename, 'w') as file:
            file.write(bad_object.to_string())
    finally:
        bad_object.delete()

我真的很难想出一个例子,其中使用 try/finally 而不是仅仅创建一个 contextmanager 是有好处的,我不确定我是否成功了(通常我会把这个例子实现为 contextmanager :-))。重要的部分应该是上下文管理器执行一个默认的清理操作,而不是本地化的自定义清理。