我有一个Python3脚本,我想选择性地重定向stdout
和stderr
到一个文件。像这样:
# variable declarations
if log_output:
output_file = open('output.txt', 'w')
sys.stdout = output_file
if log_errors:
errors_file = open('errors.txt', 'w')
sys.stderr = errors_file
# code that uses variables declared above but may exit suddenly
#at the end
if log_output:
output_file.close()
if log_errors:
errors_file.close()
这是有效的,除非我的代码在中间决定退出。那我的文件就不能保证被关闭了。无论代码中发生了什么,我如何才能干净地关闭这些文件,而且只是在某些时候?(通常,我会通过shell重定向,但我在Python中计算文件名,我不想在不同的shell中重新计算它们。另外,我不想把是否重定向的逻辑放在shell脚本中。如果可能的话,我希望这些分支在我的主代码中。)
尝试1
似乎上下文管理器将是这里的方式,但是,当我尝试使用它们时,我不得不重写我的代码几次,这不是漂亮的代码:
if log_output:
with open('output.txt', 'w') as output_file:
with contextlib.redirect_stdout(output_file):
if log_errors:
with open('errors.txt','w') as errors_file:
with contextlib.redirect_stderr(errors_file):
# log_output and log_errors
# code that uses variables declared above but may exit suddenly
else:
# log_output and not log_errors
# code that uses variables declared above but may exit suddenly
else:
if log_errors:
with open('errors.txt', 'w') as errors_file:
with contextlib.redirect_stderr(errors_file):
# not log_output and log_errors
# code that uses variables declared above but may exit suddenly
else:
# not log_output and not log_errors
# code that uses variables declared above but may exit suddenly
尝试2
我决定为它做一个上下文管理器。我认为它是有效的,Python也没有对我大喊大叫,但我还是忍不住觉得它不太像Python,我也不完全确定它是否安全。我把if
的表述往奇怪的方向推了。有没有更好的办法?
@contextlib.contextmanager
def opt_stream(stream, name = None):
if name:
file = open(name,'w')
yield file
file.close()
else:
yield stream
output_name, errors_name = None, None
if log_output:
output_name = 'outputs.txt'
if log_errors:
errors_name = 'errors.txt'
with opt_stream(sys.stdout, output_name) as output_file:
with opt_stream(sys.stderr, errors_name) as errors_file:
with contextlib.redirect_stdout(output_file):
with contextlib.redirect_stderr(errors_file):
# code that uses variables declared above but may exit suddenly
可选地将程序的
stdout
或stderr
重定向到文件的最干净的方法是在程序中根本不这样做。相反,可以通过操作系统的shell来执行。
在Linux上,如果我想将Python程序的stdout
重定向到一个文件,我会这样做:
$ python something.py > stdout.log
$ python something_else.py 2> stderr.log
注意重定向stderr
输出的2>
。
碰巧,cmd
和PowerShell在Windows上使用相同的语法
上述内容虽然正确,但与OP更新后的问题描述无关。
假设你使用的是Python 3,内置的print
函数实际上有一个命名参数" file
",可以让你决定将print
放到哪里。
print(some_object, file=your_own_file_object)
file
可以是任何类文件对象(stdout
和stderr
都是)。您可以直接传递open()
的结果,或者疯狂地使用io
模块。无论哪种情况,您只需要维护一个变量(其值可能是sys.stdout
的值)并始终将其传递给print
调用,然后在决定将内容输出到何处时设置该变量。
否则,如果您不介意被其他Python程序员嘲笑的话,您可能会考虑设置sys.stdout
和sys.stderr
的值。