我怎么知道我什么时候 can/should 使用了 `with` 关键字?

How do I know when I can/should use `with` keyword?

在C#中,当一个对象实现IDisposable时,应该使用using来保证在抛出异常时资源会被清理。例如,而不是:

var connection = new SqlConnection(...);
...
connection.Close();

一个需要写:

using (var connection = new SqlConnection(...))
{
    ...
}

因此,仅通过查看 class 的签名,我就确切地知道我是否应该在 using.

中初始化对象

在Python 3中,一个类似的结构是with。与 C# 类似,它确保在退出 with 上下文时自动清理资源,即使出现错误也是如此。

但是,我不确定我应该如何确定 with 是否应该用于特定的 class。例如,an example from psycopg 不使用 with,这可能意味着:

一般来说,在初始化特定class的实例时,我应该如何确定是否应该使用with(假设文档没有说明这个问题,并且我可以访问源代码代码)?

with 用于上下文管理器。 在代码级别,上下文管理器必须定义两个方法:

  • __enter__(self)
  • __exit__(self, type, value, traceback).

请注意,有 class 装饰器可以将原本简单的 classes/functions 转换为上下文管理器 - 请参阅 contextlib 了解一些示例

关于您何时应该使用它:

没有人强迫您使用 with 语句,它只是让您的生活更轻松的语法糖。是否使用它完全取决于您,但通常建议您这样做。 (我们很健忘,with ... 看起来比显式初始化 resource/finalize 追索调用更好)。

当您可以使用它时:

当您可以使用它时,归结为检查它是否定义了上下文管理器协议。这可能就像尝试使用 with 并看到它失败一样简单:-)

如果您需要动态检查一个对象是否是上下文管理器,您有两个选择。

首先,等待 Python 3.6 的稳定版本,它为上下文管理器定义了 ABCContextManager,可用于 issubclass/isinstance 检查:

>>> from typing import ContextManager
>>> class foo:
...     def __enter__(self): pass
...     def __exit__(self): pass
... 
>>> isinstance(foo(), ContextManager)
True
>>> class foo2: pass
... 
>>> isinstance(foo2(), ContextManager)
False

或者,创建您自己的小函数来检查它:

def iscontext(inst):
    cls = type(inst)
    return (any("__enter__" in vars(a) for a in cls.__mro__) and
            any("__exit__" in vars(a) for a in cls.__mro__))

最后一点,with 语句出现在 Python 23 中,您看到的用例可能只是没有意识到它 :-)。

只要您需要在执行语句前后执行一些类似的操作,就应该使用 with。例如:

  • 要执行SQL查询?您需要打开和关闭连接safely.Use with.
  • 要对文件执行一些操作吗? 您必须安全地打开和关闭文件。使用 with
  • 想要在临时文件中存储一些数据以执行某些任务?您需要创建目录,并在完成后清理它。使用 with,依此类推。 . .

你想在查询执行之前执行的一切,将它添加到 __enter__() 方法。以及之后要执行的操作,将其添加到 __exit__() 方法中。

with 的一个好处是,即使 with 中的代码引发任何 Exception[=34,__exit__ 也会执行=]