Python 文件上下文或 None

Python context for file or None

Python 将调用一个子进程,用户要么请求子进程 stdout 转到一个文件(或返回到 os.devnull),要么子进程输出是通过 "in real time".

我目前的最佳猜测似乎可行:


with open(file_path, 'wb') if logging else None as shell_stdout:
    subprocess.call(['ls'], stdout=shell_stdout)

在 tinkering/testing 中,这似乎是正确的值,我认为它与 subprocess.call 配合得很好。但是,毫不奇怪,我得到以下异常:

AttributeError: __exit__

所以None不是上下文,它没有__exit__


目标

那么,如何实现这种行为呢?或者你会建议做什么 instead/alternatively?

您可以创建 "no-op" context manager:

import subprocess
import contextlib
@contextlib.contextmanager
def noop():
    yield None

logging = False
file_path = '/tmp/out'

with open(file_path, 'wb') if logging else noop() as shell_stdout:
    subprocess.call(['ls'], stdout=shell_stdout)

当日志记录为True时,条件表达式returns一个文件对象。当 logging 为 False 时,它​​ returns 一个 noop() 上下文管理器(因此它可以在 with-statement 中使用),它将 shell_out 设置为 None 但在退出时没有做任何特别的事情。


Per the docs,当stdout=None,

... no redirection will occur; the child’s file handles will be inherited from the parent.

通常父级的标准输出等于 sys.stdout。但是,可以将 sys.stdout 重定向到其他地方,无论是显式(例如 sys.stdout = open('/tmp/stdout', 'wb'))还是间接,例如使用重定向 sys.stdout 的模块。例如,标准库中的 fileinput 模块重定向 sys.stdout。在这种情况下,noop() 可能有助于将标准输出定向到父级的标准输出,这可能与 sys.stdout 不同。

如果这个极端情况不影响你,那么就更简单了:

with open(file_path, 'wb') if logging else sys.stdout as shell_stdout:
    subprocess.call(['ls'], stdout=shell_stdout)

shell_stdout设置为stdoutfile object基于日志记录是真还是假,没有必要过于复杂,你只有一个条件或另一个if logging 要么是 True 要么是 False,不使用 with 打开文件没有错,有时使用 with 不合适。

import sys

if logging:
      shell_stdout = open(file_path, 'wb') # to file or devnull if logging
else:
    shell_stdout = sys.stdout
subprocess.call(['ls'], stdout=shell_stdout) # real time if not logging
shell_stdout.close()

要按照你的问题做你想做的,你可以做以下事情,你不需要任何东西吧 sys.stdout:

with open(file_path, 'wb') if logging else sys.stdout as shell_stdout:
    subprocess.call(['ls'], stdout=shell_stdout)

仅当日志记录为 True 时才会创建文件。

从 Python 3.3 开始,您可以使用 contextlib.ExitStack:

定义无操作上下文管理器
from contextlib import ExitStack

with open(...) if ... else ExitStack():
    ...