contextlib.nested中的怪癖的解决方法

时间:2012-03-25 09:22:25

标签: python python-2.7

我理解why contextlib.nested is deprecated

但是如果我为一个旧的python版本编写一个程序,而不是多种形式的(例如,< 2.7),我(几乎)别无选择。

为了避免以下构造失败:

with nested(open("f1"), open("f2")) as (f1, f2):

(如果打开f1失败,则f2将无法关闭,因为未输入上下文管理器

我可以想象编写一个上下文管理器,将初始化移动到__enter__

@contextmanager
def late_init(f, *a, **k):
    r = f(*a, **k)
    with r as c: yield c

我是否正确地想到

with nested(late_init(open, "f1"), late_init(open, "f2")) as (f1, f2):

这足以让它“干净”吗?


给出的用例只是一个例子。想象一下,你有一个文件列表,其长度不是过早知道的。那么2.7组合的with既不可用也不是2.7之前的嵌套方式,只有几个缩进的with语句。


我可能必须对此更加冗长。

上述问题乍看之下解决了问题:调用函数在安全的地方执行,以便可以检测到失败并妥善处理。

我的问题是:它是否治愈了这个缺陷,还是我遇到了其他问题?

2 个答案:

答案 0 :(得分:3)

contextlib.nested 工具用于将上下文管理器组合成一个。它被弃用了,因为它在设计上存在缺陷(正如你已经展示了如果打开 f2 失败则 f1 不会被关闭的情况)。

您使用案例虽然不需要合成。定期嵌套就足够了:

with open('f1') as f1:
    with open('f2') as f2:
        ...

答案 1 :(得分:1)

我现在看到我的解决方案(late_init())可能会治愈第一个怪癖:

  

首先,因为上下文管理器都是在之前构建的   函数被调用,内部的__new__()__init__()方法   上下文管理器实际上并未涵盖外部的范围   情境管理者。这意味着,例如,使用nested()打开   两个文件是编程错误,因为第一个文件不会被关闭   及时打开第二个文件时是否抛出异常。

但第二个:

  

其次,如果是__enter__()方法之一的内部上下文   管理者提出了一个由被抓住和压制的例外   其中一个外部上下文管理器的__exit__()方法,此构造将引发RuntimeError而不是跳过with的主体   言。

(这可能是由yield vars中跳过的nested()引起的)不在其中。

所以要么

  1. 应避免将“例外食者”与nested()结合使用,
  2. 给定的nested()应替换为更好的或
  3. 应该避免使用可变长度的异常管理器列表以及旧的Python版本。