在 python 中将 stdout 重定向到“ nothing”

我有一个大型项目,包括足够大数量的模块,每个模块打印一些东西到标准输出。现在随着项目规模的扩大,已经没有。的 print语句在标准输出上打印了很多,这使得程序相当慢。

现在,我想在 运行时间决定是否要打印任何内容到标准输出。我不能在模块中进行更改,因为有很多这样的模块。(我知道我可以将标准输出重定向到一个文件,但即使这样也相当慢。)

所以我的问题是如何将标准输出重定向到空值即如何让 print语句什么都不做?

# I want to do something like this.
sys.stdout = None         # this obviously will give an error as Nonetype object does not have any write method.

目前我唯一的想法是创建一个具有 write 方法(它什么也不做)的类,并将 stdout 重定向到该类的一个实例。

class DontPrint(object):
def write(*args): pass


dp = DontPrint()
sys.stdout = dp

在 python 中是否有一个内置的机制来解决这个问题? 还是有比这更好的方法?

114938 次浏览

如果你在 Unix 环境中(包括 Linux) ,你可以将输出重定向到 /dev/null:

python myprogram.py > /dev/null

至于 Windows:

python myprogram.py > nul

您的类可以正常工作(除了 write()方法名之外——它需要被称为 write(),小写)。只要确保将 sys.stdout的一个副本保存在另一个变量中。

如果使用的是 * NIX,则可以执行 sys.stdout = open('/dev/null'),但是它的可移植性不如滚动自己的类。

跨平台:

import os
import sys
f = open(os.devnull, 'w')
sys.stdout = f

在 Windows 上:

f = open('nul', 'w')
sys.stdout = f

在 Linux 上:

f = open('/dev/null', 'w')
sys.stdout = f

(至少在我的系统上)似乎写入 os.devnull 比写入 DontPrint 类快5倍。

#!/usr/bin/python
import os
import sys
import datetime


ITER = 10000000
def printlots(out, it, st="abcdefghijklmnopqrstuvwxyz1234567890"):
temp = sys.stdout
sys.stdout = out
i = 0
start_t = datetime.datetime.now()
while i < it:
print st
i = i+1
end_t = datetime.datetime.now()
sys.stdout = temp
print out, "\n   took", end_t - start_t, "for", it, "iterations"


class devnull():
def write(*args):
pass




printlots(open(os.devnull, 'wb'), ITER)
printlots(devnull(), ITER)

产出如下:

<open file '/dev/null', mode 'wb' at 0x7f2b747044b0>
took 0:00:02.074853 for 10000000 iterations
<__main__.devnull instance at 0x7f2b746bae18>
took 0:00:09.933056 for 10000000 iterations

这样吧:

from contextlib import ExitStack, redirect_stdout
import os


with ExitStack() as stack:
if should_hide_output():
null_stream = open(os.devnull, "w")
stack.enter_context(null_stream)
stack.enter_context(redirect_stdout(null_stream))
noisy_function()

这将使用 Contextlib模块中的特性来隐藏您试图运行的任何命令的输出,这取决于 should_hide_output()的结果,然后在该函数完成运行之后恢复输出行为。

如果您想隐藏标准错误输出,那么从 contextlib导入 redirect_stderr并添加一行代码说 stack.enter_context(redirect_stderr(null_stream))

它的主要缺点是只能在 Python 3.4和更高版本中使用。

这样做的一个好方法是创建一个小型上下文处理器,将打印内容封装在其中。然后只需在 with语句中使用 is 就可以使所有输出静音。

巨蟒2:

import os
import sys
from contextlib import contextmanager


@contextmanager
def silence_stdout():
old_target = sys.stdout
try:
with open(os.devnull, "w") as new_target:
sys.stdout = new_target
yield new_target
finally:
sys.stdout = old_target


with silence_stdout():
print("will not print")


print("this will print")

Python 3.4 + :

Python 3.4有一个内置的上下文处理器,所以您可以像下面这样简单地使用 contextlib:

import contextlib


with contextlib.redirect_stdout(None):
print("will not print")


print("this will print")

如果您想要压缩的代码使用 Nothing 作为重定向目标直接写入 sys.stdout,则此代码将无法工作。相反,你可以使用:

import contextlib
import sys
import os


with contextlib.redirect_stdout(open(os.devnull, 'w')):
sys.stdout.write("will not print")


sys.stdout.write("this will print")

如果代码写入 stderr 而不是 stdout,则可以使用 contextlib.redirect _ stderr 而不是 redirect _ stdout。


运行此代码只会打印输出的第二行,而不是第一行:

$ python test.py
this will print

这可以跨平台工作(Windows + Linux + Mac OSX) ,并且比其他解决方案更简洁。

你为什么不试试这个?

sys.stdout.close()
sys.stderr.close()

如果你使用的是 python 3.4或更高版本,有一个简单而安全的解决方案,使用标准库:

import contextlib


with contextlib.redirect_stdout(None):
print("This won't print!")
sys.stdout = None

对于 print()的情况是可以的。但是,如果调用 sys.stdout 的任何方法,例如 sys.stdout.write(),则可能会导致错误。

有一个 文件注释:

在某些条件下,stdin、 stdout 和 stderr 以及原始 值 StdinStdout真是的可以是无 Windows GUI 应用程序没有连接到控制台和 Python 应用程序从 pythonw 开始。

你可以嘲笑它。

import mock


sys.stdout = mock.MagicMock()

IFreilicht 的回答的补充——它同时适用于 python2和3。

import sys


class NonWritable:
def write(self, *args, **kwargs):
pass


class StdoutIgnore:
def __enter__(self):
self.stdout_saved = sys.stdout
sys.stdout = NonWritable()
return self


def __exit__(self, *args):
sys.stdout = self.stdout_saved


with StdoutIgnore():
print("This won't print!")

将在这里的众多答案中添加一些例子:

import argparse
import contextlib


class NonWritable:
def write(self, *args, **kwargs):
pass


parser = argparse.ArgumentParser(description='my program')
parser.add_argument("-p", "--param", help="my parameter", type=str, required=True)


#with contextlib.redirect_stdout(None): # No effect as `argparse` will output to `stderr`
#with contextlib.redirect_stderr(None): # AttributeError: 'NoneType' object has no attribute 'write'
with contextlib.redirect_stderr(NonWritable): # this works!
args = parser.parse_args()

正常产出为:

>python TEST.py
usage: TEST.py [-h] -p PARAM
TEST.py: error: the following arguments are required: -p/--param

如果不想处理资源分配或滚动自己的类,可以使用 Python 类型中的 TextIO。默认情况下,它为您提供了所有必需的存根方法。

import sys
from typing import TextIO


sys.stdout = TextIO()

这个流程中有许多好的答案,但是下面是我的 Python 3答案(当不再支持 sys.stdout.fileno ()时) :

import os
import sys
oldstdout = os.dup(1)
oldstderr = os.dup(2)
oldsysstdout = sys.stdout
oldsysstderr = sys.stderr


# Cancel all stdout outputs (will be lost) - optionally also cancel stderr
def cancel_stdout(stderr=False):
sys.stdout.flush()
devnull = open('/dev/null', 'w')
os.dup2(devnull.fileno(), 1)
sys.stdout = devnull
if stderr:
os.dup2(devnull.fileno(), 2)
sys.stderr = devnull


# Redirect all stdout outputs to a file - optionally also redirect stderr
def reroute_stdout(filepath, stderr=False):
sys.stdout.flush()
file = open(filepath, 'w')
os.dup2(file.fileno(), 1)
sys.stdout = file
if stderr:
os.dup2(file.fileno(), 2)
sys.stderr = file


# Restores stdout to default - and stderr
def restore_stdout():
sys.stdout.flush()
sys.stdout.close()
os.dup2(oldstdout, 1)
os.dup2(oldstderr, 2)
sys.stdout = oldsysstdout
sys.stderr = oldsysstderr

使用方法:

  • 用以下命令取消所有 stdout 和 stderr 输出:

    Cancel _ stdout (stderr = True)

  • 将所有 stdout (但不是 stderr)路由到一个文件:

    Reoute _ stdout (‘ output.txt’)

  • 恢复 stdout 和 stderr:

    恢复 _ stdout ()

我用这个。将 stdout重定向到字符串,然后忽略该字符串。我使用上下文管理器来保存和恢复 stdout的原始设置。

from io import StringIO
...
with StringIO() as out:
with stdout_redirected(out):
# Do your thing

其中 stdout_redirected被定义为:

from contextlib import contextmanager


@contextmanager
def stdout_redirected(new_stdout):
save_stdout = sys.stdout
sys.stdout = new_stdout
try:
yield None
finally:
sys.stdout = save_stdout