Python'with'语句 - 如何判断哪些模块/对象/类支持它?

时间:2013-04-04 19:40:37

标签: python

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”运算符是最佳实践。

  • 如何判断是否支持'with',它可以绑定什么等?
  • 另外,应该如何“提及”? (使用语句进行线程处理,使用语句进行python线程锁定,...),搜索的白话是什么,看看是否支持'with'(我们可以询问某些内容是否可迭代,我们是否可以询问它是否'可见')? / LI>

REF:

3 个答案:

答案 0 :(得分:6)

首先,你不会问某些东西是否“可以”,你会问它是否是“上下文管理器”。*

例如,在您链接的文档中(顺便提一下,来自3.1,而不是3.3):

  

目前,LockRLockConditionSemaphoreBoundedSemaphore个对象可用作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块只是一个抽象,它允许我们将设置,拆除和损坏控制代码(即“上下文管理”)分解为几种方法,然后可以在幕后被称为。这既鼓励我们分解样板,又提供更简洁,可读的代码,突出核心控制流程并隐藏实现细节。