【问题标题】:Context Manager without Yield没有 Yield 的上下文管理器
【发布时间】:2015-12-29 21:55:24
【问题描述】:

我是否可以有一个偶尔不产生的上下文管理器,在这种情况下 with 语句中的代码根本不执行?

import contextlib

@contextlib.contextmanager
def MayNotYield(to_yield):
  if to_yield:
    yield

with MayNotYield(True):  
  print 'This works.'

with MayNotYield(False):  
  print 'This errors.'

我可以要求用户用 try-catch 包装 with 语句,但这不是首选。我也可以执行以下操作,但这也很丑。

import contextlib

@contextlib.contextmanager
def AlwaysYields(to_yield):
  if to_yield:
    yield 1
  else:
    yield 2

with AlwaysYields(True) as result:
  if result == 1:
    print 'This works.'

【问题讨论】:

  • 你真正想要实现什么?
  • @jonrsharpe:我的用例是将初始化和清理代码放在上下文管理器中。上下文管理器的用户在正文中运行他们的代码。我的上下文管理器还分叉了几个进程,并且只有一个进程留给用户。所有其他进程都由库自己处理。
  • @shaoyl85:这仍然不能说明你为什么不yield。看起来您正在尝试将 if 语句与上下文管理器结合起来,以解决无法在 if 语句中分配的问题,而不是尝试管理资源。
  • 您能否在问题中解释这一点,并举一个不那么抽象的例子?这可能有助于澄清这是否是 XY problem 并为您提供更好的答案。
  • @jonrsharpe:谢谢!我相信我的问题可以有很多解决方案。我目前的问题是是否可以跳过产量。而且我认为答案确实就像 Blckknght 所说的那样,“with”语句本身没有跳过正文的选项。这不是 contextlib.contextmanager 的问题。我很擅长。

标签: python contextmanager


【解决方案1】:

另一种选择是只使用常规生成器而不是上下文管理器;普通的生成器不会有这个限制。但是您必须将它与“for”构造一起使用,而不是使用“with”:

def MayNotYield(to_yield):
   if to_yield:
      yield

for _ in MayNotYield(True):
   print('This prints.')

for _ in MayNotYield(False):
   print('This does not.')

【讨论】:

    【解决方案2】:

    鉴于在上下文管理器实现中具有条件的目标,在可以将 with 块的内容提取到其自己的函数中的情况下,还有另一种可能性。您可以将此可调用对象传递给上下文管理器,并让上下文管理器根据布尔值从上下文管理器返回传递的可调用对象或虚拟的无操作可调用对象。 with-block 将始终执行,但该操作可能会或可能不会被调用。

    def do_something():
        print("This works!")
    
    @contextlib.contextmanager
    def conditional_on(condition, f):
        if condition:
            # Acquire resources here
            pass
        else:
            # Replace the callable with a do-nothing dummy
            f = lambda x: x
        try:
            yield f
        finally:
            if condition:
                # Release resources here
                pass    
    
    with conditional_on(True, do_something) as f:
        f()  # Prints message
    
    with conditional_on(False, do_something) as f:
        f()  # Does nothing
    

    您需要根据上下文管理器正在管理的资源(如果有)以及所需的可调用签名来定制此解决方案。

    【讨论】:

      【解决方案3】:

      不幸的是,the context manager protocol 没有为上下文管理器提供一种方式来表示“不要运行with 块”(除了在__enter__ 中引发异常)。如果您无论如何都在使用上下文管理器,我认为您的第二种方法,它有 __enter__ 返回一个值来表示是否应该运行该块是最好的方法。如果您出于其他原因不需要上下文管理器,则可以使用简单的if 语句:

      if do_stuff:
          # do the stuff
      

      【讨论】:

        猜你喜欢
        • 2015-01-20
        • 2017-05-04
        • 2020-06-24
        • 1970-01-01
        • 1970-01-01
        • 2023-01-18
        • 2021-12-15
        • 1970-01-01
        • 2016-09-22
        相关资源
        最近更新 更多