【问题标题】:Using `and` instead of `,` when starting multiple context managers in `with` statement在 `with` 语句中启动多个上下文管理器时使用 `and` 而不是 `,`
【发布时间】:2021-05-09 14:57:49
【问题描述】:

在编写单元测试时,我经常使用以下模式:

with self.subTest("Invalid input") and self.assertRaises(ValueError):
  ...

但直到今天我才在这里了解到according to python specs, I should be using ,

with self.subTest("Invalid input"), self.assertRaises(ValueError):
  ...

并且规范没有提到and 作为选项。然而,测试似乎总是运行良好。

在这里使用and 可能会出现什么问题?为什么它似乎通常与, 工作方式相同?

相关:Multiple variables in a 'with' statement?

【问题讨论】:

    标签: python python-3.x syntax with-statement contextmanager


    【解决方案1】:

    您所拥有的是带有and 运算符的表达式。 and 运算符在 falsey 时返回其第一个操作数,否则返回其第二个操作数。假设self.subTest(...) 返回的东西truthy,你的代码相当于:

    ctx = self.subTest("Invalid input") and self.assertRaises(ValueError)
    with ctx: ...
    

    相当于:

    self.subTest("Invalid input")
    ctx = self.assertRaises(ValueError)
    with ctx: ...
    

    或者:

    self.subTest("Invalid input")
    with self.assertRaises(ValueError): ...
    

    因此,充其量,您所拥有的可能具有误导性。在最坏的情况下这是一个错误,因为 subTest 的上下文管理器没有被使用。

    如果 self.subTest 返回 falsey 值,则永远不会执行 self.assertRaises(...),这将是您的测试中的一个明显错误。

    【讨论】:

      【解决方案2】:

      除了@deceze 的出色回答之外,您可以很容易地观察到这一点:您只需要创建自己的上下文管理器。

      运行以下代码:

      class C:
          def __init__(self, id):
              self.id = id
          def __enter__(self):
              print(f'Entering {self.id}')
              return self
          def __exit__(self, exc_type, exc_value, traceback):
              print(f'Exiting {self.id}')
              return self
      
      with C(0), C(1): print('with statement 1')
      print()
      with C(2) and C(3): print('with statement 2')
      

      您会看到打印了以下输出:

      Entering 0
      Entering 1
      with statement 1
      Exiting 1
      Exiting 0
      
      Entering 3
      with statement 2
      Exiting 3
      

      发生的情况是,对于第一个 with 语句,两个值按顺序输入,然后以相反的顺序退出(这些是 Python 的 with 语句的语义)。但是在第二个with 中,只处理了第二个项目(进入然后退出)——因为第一个项目被and 运算符丢弃了。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-05-06
        • 1970-01-01
        • 2012-08-07
        • 2017-03-29
        • 2016-05-30
        • 2011-03-02
        • 1970-01-01
        相关资源
        最近更新 更多