临时重定向stdout/stderr

77 投票
9 回答
82168 浏览
提问于 2025-04-16 22:06

在Python中,有没有办法暂时改变标准输出和错误输出的方向呢?也就是说,只在某个方法执行的时候改变它们的输出?

编辑:

现在的解决方案有个问题(我一开始记得,但后来又忘了),就是它们并不是在“重定向”;而是直接把整个输出流替换掉了。因此,如果某个方法出于某种原因有一个局部副本的变量(比如这个输出流是作为参数传给了其他东西),那就不管用了。

有没有什么解决办法呢?

9 个回答

38

从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...
98

你也可以把重定向的逻辑放在一个上下文管理器里。

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!")
25

为了处理某些函数可能把 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() 这个函数可以把所有输出重定向到指定的文件名、文件对象或者文件描述符(在这个例子中是 os.devnull)。

stdout_redirected()merged_stderr_stdout() 的定义可以在这里找到

撰写回答