【问题标题】:Hints for nested context managers in a Python 2.6- / Python 3-neutral way?以 Python 2.6/Python 3 中立的方式嵌套上下文管理器的提示?
【发布时间】:2013-10-08 04:52:16
【问题描述】:

我一直在使用许多上下文管理器作为组合各种设置/拆卸情况的简洁方式。由于我的部署以 Python 2.6 为目标,这意味着使用 contextlib.nested

最近我对使用相同的代码库同时支持 Python 2.x 和 Python 3 很感兴趣。 some projects 可以做到这一点,但在上下文管理器的情况下我遇到了麻烦,因为:

  1. contextlib.nested 在 Python 3 中不受支持
  2. 2.6 不支持 Python-3 样式的嵌套上下文管理器(例如,with aa() as a, bb() as b: ...)。

这里有一个基本的语法不兼容。由于我无法控制的各种原因,2.7 目前可能难以投入生产,但我想尽可能地对代码进行未来验证,因此我对 Python 3 感兴趣。

谁能建议一种解决方法,在 2.6 和 3.x 的同一代码库中支持嵌套上下文管理器?或者这是一个失败的原因?

【问题讨论】:

    标签: python python-3.x python-2.6 contextmanager


    【解决方案1】:

    来自文档:

    这个函数有两个主要的怪癖导致它被弃用。首先,由于上下文管理器都是在调用函数之前构建的,因此内部上下文管理器的__new__()__init__()方法实际上并没有被外部上下文管理器的范围覆盖。这意味着,例如,使用nested() 打开两个文件是一个编程错误,因为如果在打开第二个文件时抛出异常,第一个文件将不会立即关闭。

    其次,如果内部上下文管理器之一的__enter__() 方法引发异常,该异常被外部上下文管理器之一的__exit__() 方法捕获和抑制,则此构造将引发RuntimeError 而不是跳过with 语句的主体。

    因此,几乎所有情况下,正确答案都是 JBernardo 的。缩进多一点,但错误也少一点。

    【讨论】:

    • 赞成,这很有帮助,我应该更密切地使用 RTFM。谢谢。
    • 我相信比我聪明的人都已经想到了,但是我想知道为什么这些问题不能通过递归写nested来避免,让每个经理真的是在嵌套范围内调用。
    • @asmeurer:问题不在于范围,而在于__exit__() 的工作方式。如果您想搜索它,python-dev 档案中会对该问题和各种潜在的解决方案进行长时间的讨论。但结论是,与其修复它,不如放弃它。
    【解决方案2】:

    只是嵌套它们

    with aa() as a:
        with bb() as b:
            #some code here
    

    【讨论】:

    • 这对情侣来说很好。但是我有一些更冗长的组合(集成测试),当它们组合在一个语句中时会更清晰。
    • @JohnJ 如果你真的想要contextlib.nested,请抓住source code 并粘贴到你的项目中。
    • @JohnJ:如果你真的有一个动态的上下文管理器列表,那么你确实需要像nested这样的东西——尽管即使那样,你也可以写一些更简单的东西如果你不需要处理所有的边缘情况nested 尝试处理但失败了,那就不那么古怪了。
    • @JohnJ:另一方面,如果您有一个静态但很大的集合,请考虑编写一个包装器上下文管理器来明确处理该特定集合,那么您只需要一个简单的with声明。
    • 是的,我以前做过,而且鉴于@Veedrac 的回答,我显然会做更多的事情。谢谢!
    【解决方案3】:

    如果 Veedrac 提到的 nested 的怪癖对您来说不是问题,您可以从 Python 标准库中复制 code

    如果它们确实困扰您,那么您唯一的选择是手动嵌套它们,或者放弃对 Python 2.6 的支持。如果您为此使用两个代码库或一个代码库,这并不重要。如果是这种情况,那么它在 Python 2.6 中的only 方式就是嵌套它们。我想您可以尝试编写某种自定义 2to3 修复程序,将未嵌套的 2.7 代码转换为嵌套的 2.6 代码。但老实说,在您放弃 2.6 支持之前,只使用带有嵌套管理器的单个代码库会不会那么痛苦。

    【讨论】:

      【解决方案4】:

      您始终可以自行重新实现nested,并将其保存在项目内的compatibility.py 文件中。这通常是跨版本所做的。

      编辑:我看到@JBernardo 已经在评论中提到了这个解决方案。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-09-18
        • 1970-01-01
        • 2011-12-25
        • 1970-01-01
        • 1970-01-01
        • 2019-06-18
        相关资源
        最近更新 更多