【问题标题】:Is it possible to have an optional with/as statement in python?是否可以在 python 中有一个可选的 with/as 语句?
【发布时间】:2021-10-14 08:40:12
【问题描述】:

而不是这个:

FILE = open(f)
do_something(FILE)
FILE.close()

最好用这个:

with open(f) as FILE:
    do_something(FILE)

如果我有这样的东西怎么办?

if f is not None:
   FILE = open(f)
else:
   FILE = None
do_something(FILE)
if FILE is not None:
    FILE.close()

do_something 也有一个“if FILE is None”子句,并且在这种情况下仍然会做一些有用的事情 - 我只想跳过 do_something 如果 FILE 是 None。

有没有一种明智的方法可以将其转换为 with/as 形式?还是我只是试图以错误的方式解决可选文件问题?

【问题讨论】:

    标签: python file-io with-statement


    【解决方案1】:

    如果你只是这样写:

    if f is not None:
        with open(f) as FILE:
            do_something(FILE)
    else:
        do_something(f)
    

    file 是内置的顺便说一句)

    更新

    这是一种使用不会崩溃的可选 None 来执行即时上下文的时髦方法:

    from contextlib import contextmanager
    
    none_context = contextmanager(lambda: iter([None]))()
    # <contextlib.GeneratorContextManager at 0x1021a0110>
    
    with (open(f) if f is not None else none_context) as FILE:
        do_something(FILE)
    

    它创建一个返回 None 值的上下文。 with 将生成 FILE 作为文件对象或 None 类型。但是 None 类型将有一个正确的__exit__

    更新

    如果您使用 Python 3.7 或更高版本,则可以以更简单的方式声明 null 上下文管理器以用于替代目的:

    import contextlib
    none_context = contextlib.nullcontext()
    

    您可以在此处阅读有关这些内容的更多信息:

    https://docs.python.org/3.7/library/contextlib.html#contextlib.nullcontext

    【讨论】:

    • @sberry - 我看到我们几乎拥有相同的东西,彼此非常接近:-)
    • 我希望避免重复 do_something,因为在实际情况下它是一大块代码(尽管我可以将它粘贴到函数中,等等)。但也许这是我能做的最好的了。
    • 我刚刚添加了一个有趣的小例子,说明如何做你想做的事。
    • 注意none_context只能使用一次。我更喜欢让它成为可调用的:def none_context(a=None): return contextmanager(iter)([a])
    • 此外,如果引发异常 (AttributeError: 'listiterator' object has no attribute 'throw'),它将失败。这更健壮:def none_context(a=None): return contextmanager(lambda: (x for x in [a]))()
    【解决方案2】:

    从 Python 3.7 开始,你也可以这样做

    from contextlib import nullcontext
    
    with (open(file) if file else nullcontext()) as FILE:
        # Do something with `FILE`
        pass
    

    有关详细信息,请参阅official documentation

    【讨论】:

      【解决方案3】:

      这似乎解决了你所有的顾虑。

      if file_name is not None:
          with open(file_name) as fh:
              do_something(fh)
      else:
              do_something(None)
      

      【讨论】:

      • 我希望避免重复do_something,因为在实际情况下它是一个更大的代码块(尽管我可以将它粘贴在一个函数中,等等)。但也许这是我能做的最好的了。
      【解决方案4】:

      类似:

      if file:      #it checks for None,false values no need of "if file is None"
          with open(file) as FILE:
              do_something(FILE)
      else:
          FILE=None
      

      【讨论】:

      • 对不起,我没有说得很清楚。希望问题的编辑版本更有意义。我实际上希望 do_something 运行,即使 FILE 是 None - 它还有其他有用的功能,它的文件相关部分是可选的。
      【解决方案5】:

      在 Python 3.3 及以上版本中,您可以使用contextlib.ExitStack 很好地处理这种情况

      with contextlib.ExitStack() as stack:
          FILE = stack.enter_context(open(f)) if f else None
          do_something(FILE)
      

      【讨论】:

        【解决方案6】:

        Python 3.7 支持contextlib.nullcontext,可用于避免创建自己的虚拟上下文管理器。

        此示例展示了如何有条件地打开文件或使用stdout

        import contextlib
        import sys
        
        def write_to_file_or_stdout(filepath=None, data):
            with (
                    open(filepath, 'w') if filepath is not None else
                    contextlib.nullcontext(sys.stdout)
            ) as file_handle:
                file_handle.write(data)
        

        contextlib.nullcontext() 如果值可以为 None,则可以不带任何参数调用。

        【讨论】:

          【解决方案7】:

          虽然所有其他答案都很好,而且更可取,但请注意with 表达式可能是任何 表达式,因此您可以这样做:

          with (open(file) if file is not None else None) as FILE:
              pass
          

          请注意,如果评估了 else 子句,产生 None 这将导致异常,因为 NoneType 不支持将适当的操作用作上下文管理器。

          【讨论】:

          • 哦,太棒了!我没有意识到这一点。看起来这就是我需要的。
          • 等等...你确定这行得通吗? None 类型会崩溃,因为它没有 AttributeError: __exit__
          • @jdi - 该死,你是对的,它确实崩溃了......回到绘图板。 :(
          • @jdi:公平点。不过,我想在这里强调语法和可能性。我将进行编辑以明确这将在执行时失败。
          • @Marcin:我最终想出了一个即时的 NoneType 上下文,在我的回答中,它的工作方式与您的预期一致。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-02-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多