Raymond Hettinger在展示幻灯片36和37时让很多人感到惊讶。https://speakerdeck.com/pyconslides/transforming-code-into-beautiful-idiomatic-python-by-raymond-hettinger - 很多人都知道with语句可用于打开文件,但不能用于这些新事物。关于线程的python 3.3文档,仅在最底层,第16.2.8节,甚至提到它。从讲座中可以看出,使用“with”运算符是最佳实践。
REF:
答案 0 :(得分:6)
首先,你不会问某些东西是否“可以”,你会问它是否是“上下文管理器”。*
例如,在您链接的文档中(顺便提一下,来自3.1,而不是3.3):
目前,
Lock
,RLock
,Condition
,Semaphore
和BoundedSemaphore
个对象可用作with
语句上下文管理器。< / p>
同时,如果你想在交互式翻译中搜索,有两件显而易见的事情要做:
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中,它只返回一个东西(file
),但help
会告诉您它返回的确切内容,然后您可以在适合您的用例的任何一个上使用help
,或者只看其属性,看它定义__exit__
。
无论如何,实际上,只要记住EAFTP适用于调试和原型设计以及最终代码。尝试先用with
语句写一些东西。如果您尝试用作上下文管理器的表达式不是一个,那么只要您尝试运行该代码就会出现异常,这很容易调试。 (关于缺少AttributeError
通常会__exit__
,但即使不是,但追溯说来自with
行的事实应该告诉你问题。)如果你有一个似乎应该可用作上下文管理器的对象,而不是,你可能想要考虑提交一个bug /在邮件列表上提交/等等。 (在有人抱怨之前,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
(如果你有{{1}使用stdlib函数可以使你的意图清晰。
*实际上,关于命名存在一些争论,并且它经常在邮件列表上反复出现。但是文档和help('with')
都给出了几乎精确的定义,“上下文管理器”是评估“上下文表达”的结果。因此,在with foo(bar) as baz, qux as quux:
中,foo(bar)
和qux
都是上下文管理器。 (或者可能以某种方式,他们两个组成一个上下文管理器。)
答案 1 :(得分:2)
afaik任何实现__exit__
方法的类/对象(您可能还需要实现__enter__
)
>>>dir(file)
#notice it includes __enter__ and __exit__
所以
def supportsWith(some_ob):
if "__exit__" in dir(some_ob): #could justas easily used hasattr
return True
答案 2 :(得分:1)
使用Python的with
语句的对象称为上下文管理器。通常以Pythonic方式,对象是否是上下文管理器,仅取决于您是否可以使用它来执行“上下文管理器”事务。 (此策略称为duck typing。)
那么什么构成了“上下文管理器-y”行为?有两件事:(1)在进入with
区块时进行一些标准设置,以及(2)做一些标准的“拆卸”,如果出现问题,可能还会进行一些损坏控制,之前退出街区。就是这样。
详细信息在PEP 343中提供,其中介绍了with
语句以及您在问题中链接的文档。
with
块,一步一步但让我们一步一步地完成这一过程。
首先,我们需要一个“上下文管理器”。这是提供封装在分别称为__enter__
和__exit__
的方法中的设置和拆除行为的任何对象。如果一个对象提供了这些方法,它就有资格作为一个上下文管理器,如果这些方法不能做出明智的事情,那么它可能是一个很差的。
那么当解释器看到with
块时,幕后会发生什么?首先,解释器在__enter__
语句后面提供的对象上查找__exit__
和with
方法。如果方法不存在,那么我们没有上下文管理器,因此解释器抛出异常。
但如果方法确实存在,一切都很好。我们有上下文管理器,所以我们进入块。解释器然后执行上下文管理器的__enter__
方法,并将结果分配给as
语句后面的变量(如果有的话,否则结果将被丢弃)。接下来,执行with
块的主体。完成后,上下文管理器的__exit__
语句将传递一个(可能是空的)字典,其中包含有关执行bock主体时发生的任何异常的信息,并执行__exit__
方法来清理内容。 / p>
这是一个逐行演练:
with man as x: # 1) Look for `man.__enter__` and `man.__exit__`, then ...
# 2) Execute `x = man.__enter__()`, then ...
do_something(x) # 3) Execute the code in the body of the block, ...
# 4) If something blows up, note it (in `err`), ...
# 5) Last, this (always!) happens: `man.__exit__(**err)`.
carry_on()
就是这样。
这里唯一的细微之处在于,即使在__exit__
块的主体中抛出了未捕获的异常,上下文管理器的with
方法仍然执行。在这种情况下,异常信息将传递给__exit__
方法(在我上面示例中称为err
的字典中),该方法应该使用该信息来提供损害控制。
所以with
块只是一个抽象,它允许我们将设置,拆除和损坏控制代码(即“上下文管理”)分解为几种方法,然后可以在幕后被称为。这既鼓励我们分解样板,又提供更简洁,可读的代码,突出核心控制流程并隐藏实现细节。