首先,你不问某事是否“可使用”,而是问它是否是“上下文管理器”。*
例如,在您链接的文档中(顺便说一下,它们来自 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
上下文管理器。(或者也许在某种程度上,他们两个组成了一个上下文管理器。)