临时重定向 stdout/stderr

是否有可能在 Python 中临时重定向 stdout/stderr (即在方法期间) ?

编辑:

当前解决方案的问题(我一开始记得,但后来忘了)在于它们不是 再次询问; 相反,它们只是完整地替换了流。因此,如果一个方法由于某种原因(例如,因为流被作为参数传递给某个东西)而使变量的 < em > 本地 副本为1,那么它就不会工作。

有什么办法吗?

74111 次浏览

我不确定临时重定向是什么意思。但是,您可以像这样重新分配流并重新设置它。

temp = sys.stdout
sys.stdout = sys.stderr
sys.stderr = temp

还可以在 print stmts 中写入 sys.stderr,如下所示。

 print >> sys.stderr, "Error in atexit._run_exitfuncs:"

定期打印将标准输出。

可以使用如下的装饰工具:

import sys


def redirect_stderr_stdout(stderr=sys.stderr, stdout=sys.stdout):
def wrap(f):
def newf(*args, **kwargs):
old_stderr, old_stdout = sys.stderr, sys.stdout
sys.stderr = stderr
sys.stdout = stdout
try:
return f(*args, **kwargs)
finally:
sys.stderr, sys.stdout = old_stderr, old_stdout


return newf
return wrap

用途:

@redirect_stderr_stdout(some_logging_stream, the_console):
def fun(...):
# whatever

或者,如果不想修改 fun的源代码,可以直接调用它作为

redirect_stderr_stdout(some_logging_stream, the_console)(fun)

但是请注意,这不是线程安全的。

还可以将重定向逻辑放在上下文管理器中。

import os
import sys


class RedirectStdStreams(object):
def __init__(self, stdout=None, stderr=None):
self._stdout = stdout or sys.stdout
self._stderr = stderr or sys.stderr


def __enter__(self):
self.old_stdout, self.old_stderr = sys.stdout, sys.stderr
self.old_stdout.flush(); self.old_stderr.flush()
sys.stdout, sys.stderr = self._stdout, self._stderr


def __exit__(self, exc_type, exc_value, traceback):
self._stdout.flush(); self._stderr.flush()
sys.stdout = self.old_stdout
sys.stderr = self.old_stderr


if __name__ == '__main__':


devnull = open(os.devnull, 'w')
print('Fubar')


with RedirectStdStreams(stdout=devnull, stderr=devnull):
print("You'll never see me")


print("I'm back!")

这里有一个我觉得很有用的上下文管理器。这样做的好处是,您可以将它与 with语句一起使用,并且它还可以处理子进程的重定向。

import contextlib




@contextlib.contextmanager
def stdchannel_redirected(stdchannel, dest_filename):
"""
A context manager to temporarily redirect stdout or stderr


e.g.:


with stdchannel_redirected(sys.stderr, os.devnull):
...
"""


try:
oldstdchannel = os.dup(stdchannel.fileno())
dest_file = open(dest_filename, 'w')
os.dup2(dest_file.fileno(), stdchannel.fileno())


yield
finally:
if oldstdchannel is not None:
os.dup2(oldstdchannel, stdchannel.fileno())
if dest_file is not None:
dest_file.close()

为什么我创建这个的上下文是在 这篇博文

为了解决某个函数可能将 sys.stdout流缓存为局部变量,因此替换全局 sys.stdout在该函数内部不起作用的问题,可以在文件描述符级别(sys.stdout.fileno())进行重定向,例如:

from __future__ import print_function
import os
import sys


def some_function_with_cached_sys_stdout(stdout=sys.stdout):
print('cached stdout', file=stdout)


with stdout_redirected(to=os.devnull), merged_stderr_stdout():
print('stdout goes to devnull')
some_function_with_cached_sys_stdout()
print('stderr also goes to stdout that goes to devnull', file=sys.stderr)
print('stdout is back')
some_function_with_cached_sys_stdout()
print('stderr is back', file=sys.stderr)

stdout_redirected() sys.stdout.fileno()的所有输出重定向到给定的文件名、文件对象或文件描述符(示例中为 os.devnull)。

这里定义了 stdout_redirected()merged_stderr_stdout()

我们将在 python3中使用 开始B _ get _ content函数的 PHP语法,并将输入重定向到一个文件中。

输出存储在一个文件中,任何类型的流都可以使用。

from functools import partial
output_buffer = None
print_orig = print
def ob_start(fname="print.txt"):
global print
global output_buffer
print = partial(print_orig, file=output_buffer)
output_buffer = open(fname, 'w')
def ob_end():
global output_buffer
close(output_buffer)
print = print_orig
def ob_get_contents(fname="print.txt"):
return open(fname, 'r').read()

用法:

print ("Hi John")
ob_start()
print ("Hi John")
ob_end()
print (ob_get_contents().replace("Hi", "Bye"))

会打印出来

嗨,约翰 再见,约翰

从 python 3.4开始,有一个上下文管理器 contextlib.redirect_stdout:

from contextlib import redirect_stdout


with open('yourfile.txt', 'w') as f:
with redirect_stdout(f):
# do stuff...

stdout完全沉默这个方法可行:

from contextlib import redirect_stdout


with redirect_stdout(None):
# do stuff...

看看 Redirect _ stdout (new _ target)Redirect _ stderr (new _ target)redirect_stderr在 Python 3.5中是新的。