file或None的Python上下文



Python要调用子流程,用户要么请求子流程stdout转到文件(或返回os.devnull),要么子流程输出通过"实时"传递。

我目前对如何做到这一点的最佳猜测似乎是可行的:

  • file_pathopen()的有效输入
  • logging为布尔指示符,true表示使用file_path进行日志记录,false表示传递到stdout

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

在修补/测试中,这似乎是正确的值,我认为它与subprocess.call配合良好。然而,不出所料,我得到了以下异常:

AttributeError: __exit__

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


目标

  • 如果用户不希望进行日志记录,则根本不打开文件
  • 使用上下文(由stdlib提供),(Preference;我无法想象手动执行文件打开/关闭操作会更干净。)
  • 不需要try/catch(首选避免进一步嵌套)
  • 仅对subprocesses.call(非重复线路)进行一次调用

那么,这种行为是如何实现的呢?或者你建议你做些什么?

您可以创建一个"无操作"上下文管理器:

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时,条件表达式返回一个文件对象。当logging为False时,它返回一个noop()上下文管理器(因此它可以在with-statement中使用),该管理器将shell_out设置为None,但在退出时不执行任何特殊操作。


根据文档,当stdout=None

不会发生重定向;子级的文件句柄将从父级继承。

通常,父对象的stdout等于sys.stdout。然而,可以将sys.stdout重定向到其他地方,无论是明确地(例如sys.stdout = open('/tmp/stdout', 'wb'))还是间接地,例如通过使用重定向sys.stdout的模块。例如,来自标准库的fileinput模块重定向sys.stdout。在这种情况下,noop()可能有助于将stdout引导到父级的stdout,这可能与sys.stdout不同。

如果这个角落的情况不影响你,那么Padraic Cunningham的解决方案更简单:

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

根据日志记录为True或False,将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开始,tou可以使用contextlib.ExitStack:定义无操作上下文管理器

from contextlib import ExitStack
with open(...) if ... else ExitStack():
    ...

最新更新