Python中的条件语句

nic*_*ole 55 python conditional indentation with-statement conditional-statements

有没有办法用with语句开始一段代码,但是有条件地?

就像是:

if needs_with():
    with get_stuff() as gs:

# do nearly the same large block of stuff,
# involving gs or not, depending on needs_with()
Run Code Online (Sandbox Code Playgroud)

为了澄清,一种情况会在with语句中包含一个块,而另一种情况可能是同一个块,但不包含(即,好像它没有缩进)

最初的实验当然会给出压痕错误..

Mik*_*ike 53

Python 3.3 contextlib.ExitStack就是针对这种情况而引入的.它为您提供了一个"堆栈",您可以根据需要向其添加上下文管理器.在你的情况下,你会这样做:

from contextlib import ExitStack

with ExitStack() as stack:
    if needs_with():
        gs = stack.enter_context(get_stuff())

    # do nearly the same large block of stuff,
    # involving gs or not, depending on needs_with()
Run Code Online (Sandbox Code Playgroud)

输入的任何内容都会像往常一样在语句末尾stack自动exit编辑with.(如果没有输入任何内容,则这不是问题.)在此示例中,自动返回的内容get_stuff()均为exited.

如果你必须使用早期版本的python,你可能可以使用该contextlib2模块,虽然这不是标准的.它将此功能和其他功能向后移植到早期版本的python.如果您喜欢这种方法,您甚至可以进行条件导入.

  • +1,这应该是选定的答案.正如[这里](https://docs.python.org/3/library/contextlib.html?highlight=contextmanager#simplifying-support-for-single-optional-context-managers)指出的那样,它是为了处理这种问题.此外,它可以用作漂亮的单行:`get_stuff()if needs_with()else ExitStack()as gs`. (10认同)
  • IMO 最好针对此用例使用专门构建的“nullcontext”:“with get_stuff() if need_with() else nullcontext() as gs”。`ExitStack` 用于对进入和退出上下文进行更复杂的编程控制,即在文档中,“这是一个相对较低级别的 API”,这在这里是多余的。 (3认同)

jam*_*lin 47

如果你想避免重复代码并使用3.7之前(contextlib.nullcontext引入时)或3.3(引入时)的Python版本contextlib.ExitStack,你可以做类似的事情:

class dummy_context_mgr():
    def __enter__(self):
        return None
    def __exit__(self, exc_type, exc_value, traceback):
        return False
Run Code Online (Sandbox Code Playgroud)

要么:

import contextlib

@contextlib.contextmanager
def dummy_context_mgr():
    yield None
Run Code Online (Sandbox Code Playgroud)

然后将其用作:

with get_stuff() if needs_with() else dummy_context_mgr() as gs:
   # do stuff involving gs or not
Run Code Online (Sandbox Code Playgroud)

你也可以get_stuff()根据自己做出不同的回报needs_with().

(请参阅Mike的回答Daniel的回答,了解您在以后的版本中可以做些什么.)

  • 这个上下文管理器应该在标准的python库中,imho.谢谢你. (3认同)
  • @jjmontes [contextlib.ExitStack](https://docs.python.org/3/library/contextlib.html#simplifying-support-for-single-optional-context-managers)(新的python 3.3)可以用作虚拟上下文管理器。 (2认同)

Dan*_*ous 14

从Python 3.7开始,您可以使用contextlib.nullcontext:

from contextlib import nullcontext

if needs_with():
    cm = get_stuff()
else:
    cm = nullcontext()

with cm as gs:
    # Do stuff
Run Code Online (Sandbox Code Playgroud)

contextlib.nullcontext几乎只是一个无操作的上下文管理器.你可以传递一个它将产生的参数,如果你依赖于以下之后存在的东西as:

>>> with nullcontext(5) as value:
...     print(value)
...
5
Run Code Online (Sandbox Code Playgroud)

否则它只会返回None:

>>> with nullcontext() as value:
...     print(value)
...
None
Run Code Online (Sandbox Code Playgroud)

它非常整洁,请查看以下文档:https://docs.python.org/3/library/contextlib.html#contextlib.nullcontext

  • 但这提出了一个问题,在输入 with 语句*之前*调用 get_stuff() 总是安全的吗?`with open(file) as fh` 相当于 `f = open(file)` 后跟 `with f as fh` 吗? (4认同)
  • 这取决于您的上下文管理器的作用。大多数上下文管理器不应该在它们的 __init__` 方法中执行操作,而只在它们的 __enter__` (或 __aenter__`)方法中执行操作,该方法在 `with` 语句中使用时被调用。所以不幸的是,答案是“视情况而定”。如果您担心它,您可以将函数分配给“cm”而不调用它们(如果需要,使用“functools.partial”),然后执行“with cm() as gs”。 (2认同)
  • 一行代码是:`with get_stuff() if need_with() else nullcontext() as gs` (2认同)

Ane*_*pic 10

实现此目的的第三方选项:https:
//pypi.python.org/pypi/conditional

from conditional import conditional

with conditional(needs_with(), get_stuff()):
    # do stuff
Run Code Online (Sandbox Code Playgroud)


Luc*_*uez 6

import contextlib

my_context = None # your context
my_condition = False # your condition

# Option 1 (Recommended)
with my_context if my_condition else contextlib.nullcontext():
    print('hello 1')

# Option 2
with my_context if my_condition else contextlib.ExitStack():
    print('hello 2')
Run Code Online (Sandbox Code Playgroud)