首先,你不问某事是否“可使用”,而是问它是否是“上下文管理器”。*
例如,在您链接的文档中(顺便说一下,它们来自 3.1,而不是 3.3):
目前,Lock、RLock、Condition、Semaphore和BoundedSemaphore对象可以用作with语句上下文管理器。
同时,如果你想在交互式解释器中进行搜索,有两件显而易见的事情要做:
if hasattr(x, '__exit__'):
print('x is a context manager')
try:
with x:
pass
except AttributeError:
pass
else:
print('x is a context manager')
同时:
help(open)……没有提及
嗯,是的,因为open它不是上下文管理器,它是一个碰巧返回上下文管理器的函数。在 3.3 中,它可以根据其参数返回各种不同的东西;在 2.7 中,它只返回一件事 (a file),但help会告诉您它返回的确切内容,然后您可以使用help适合您的用例的任何一个,或者只查看它的属性,看看它定义了__exit__.
无论如何,实际上,请记住 EAFTP 适用于调试和原型设计以及您的最终代码。尝试先写一些带有with声明的东西。如果您尝试用作上下文管理器的表达式不是一个,那么您将在尝试运行该代码时立即收到异常,这很容易调试。(这通常是AttributeError关于缺少的__exit__,但即使不是,回溯表明它来自您的with线路的事实应该告诉您问题。)如果您有一个看起来应该可用的对象作为上下文管理器,但不是,您可能需要考虑提交错误/将其提交到邮件列表/等。(在有人抱怨之前,stdlib 中有一些类不是上下文管理器。)
最后一件事:如果您使用的类型具有close方法,但不是上下文管理器,请contextlib.closing在它周围使用:
with closing(legacy_file_like_object):
… 或者
with closing(legacy_file_like_object_producer()) as f:
实际上,您应该真正查看contextlib. @contextmanager非常漂亮,nested如果您需要将 2.7/3.x 代码反向移植到 2.5,并且虽然closing编写起来很简单(如果您有@contextmanager),但使用 stdlib 函数可以使您的意图变得清晰。
* 实际上,关于命名存在一些争论,并且它经常在邮件列表中重复出现。但是文档和help('with')两者都给出了近乎精确的定义,“上下文管理器”是评估“上下文表达式”的结果。因此, inwith foo(bar) as baz, qux as quux:和foo(bar)都是qux上下文管理器。(或者也许在某种程度上,他们两个组成了一个上下文管理器。)