在 Python 中使函数的 stdout 保持静默,而不会损坏 sys.stdout 并恢复每个函数调用

在 Python 中有没有一种方法可以让 stdout 静默,而不用像 follow 那样包装函数调用?

原始破解代码:

from sys import stdout
from copy import copy
save_stdout = copy(stdout)
stdout = open('trash','w')
foo()
stdout = save_stdout

编辑: 来自 Alex Martelli 的更正代码

import sys
save_stdout = sys.stdout
sys.stdout = open('trash', 'w')
foo()
sys.stdout = save_stdout

这种方法可行,但效率似乎非常低。有 已经更好的方法。有什么办法吗?

67786 次浏览

假设 foo包含 print语句,那么在您正在做的时候赋值 stdout变量没有任何效果——这是另一个例子,说明为什么您永远不应该从 在里面导入模块(正如您在这里所做的那样) ,而总是导入整个模块(然后使用限定名称)。顺便说一句,copy无关紧要。正确的代码片段是:

import sys
save_stdout = sys.stdout
sys.stdout = open('trash', 'w')
foo()
sys.stdout = save_stdout

现在 ,当代码是正确的时候,就是让它更优雅或更快的时候了。例如,您可以使用内存中类似文件的对象而不是文件“垃圾”:

import sys
import io
save_stdout = sys.stdout
sys.stdout = io.BytesIO()
foo()
sys.stdout = save_stdout

对于优雅来说,背景是最好的,例如:

import contextlib
import io
import sys


@contextlib.contextmanager
def nostdout():
save_stdout = sys.stdout
sys.stdout = io.BytesIO()
yield
sys.stdout = save_stdout

一旦定义了这个上下文,对于不需要标准输出的任何块,

with nostdout():
foo()

更多优化: 您只需要将 sys.stdout 替换为一个具有 no-op write方法的对象:

import contextlib
import sys


class DummyFile(object):
def write(self, x): pass


@contextlib.contextmanager
def nostdout():
save_stdout = sys.stdout
sys.stdout = DummyFile()
yield
sys.stdout = save_stdout

使用方式与以前的 nostdout实现相同。我认为没有比这更干净或更快的了。

你为什么认为这是低效的?你 测试它吗?顺便说一下,它根本不起作用,因为您使用的是 from ... import语句。 替换 sys.stdout是可以的,但是不要复制,也不要使用临时文件。打开 null 设备:

import sys
import os


def foo():
print "abc"


old_stdout = sys.stdout
sys.stdout = open(os.devnull, "w")
try:
foo()
finally:
sys.stdout.close()
sys.stdout = old_stdout

Alex Martelli 的回答的一个小小的修改。

这解决了始终要为函数禁止 stdout而不是单独调用函数的情况。

如果多次调用 foo(),将函数包装(装饰它)可能会更好/更容易。这样,您只需更改 foo的定义一次,而不是将函数的每次使用都封装在 with 语句中。

import sys
from somemodule import foo


class DummyFile(object):
def write(self, x): pass


def nostdout(func):
def wrapper(*args, **kwargs):
save_stdout = sys.stdout
sys.stdout = DummyFile()
func(*args, **kwargs)
sys.stdout = save_stdout
return wrapper


foo = nostdout(foo)

通过进一步推广,您可以得到一个很好的装饰器,它可以捕获输出,甚至返回输出:

import sys
import cStringIO
from functools import wraps


def mute(returns_output=False):
"""
Decorate a function that prints to stdout, intercepting the output.
If "returns_output" is True, the function will return a generator
yielding the printed lines instead of the return values.


The decorator litterally hijack sys.stdout during each function
execution for ALL THE THREADS, so be careful with what you apply it to
and in which context.


>>> def numbers():
print "42"
print "1984"
...
>>> numbers()
42
1984
>>> mute()(numbers)()
>>> list(mute(True)(numbers)())
['42', '1984']


"""


def decorator(func):


@wraps(func)
def wrapper(*args, **kwargs):


saved_stdout = sys.stdout
sys.stdout = cStringIO.StringIO()


try:
out = func(*args, **kwargs)
if returns_output:
out = sys.stdout.getvalue().strip().split()
finally:
sys.stdout = saved_stdout


return out


return wrapper


return decorator

我认为没有比这更干净或更快的了; -)

呸! 我想我可以做得更好一点:-D

import contextlib, cStringIO, sys


@contextlib.contextmanager
def nostdout():


'''Prevent print to stdout, but if there was an error then catch it and
print the output before raising the error.'''


saved_stdout = sys.stdout
sys.stdout = cStringIO.StringIO()
try:
yield
except Exception:
saved_output = sys.stdout
sys.stdout = saved_stdout
print saved_output.getvalue()
raise
sys.stdout = saved_stdout

它得到了我最初想要的结果,即正常地抑制输出,但是如果抛出错误,则显示被抑制的输出。

为了补充其他人已经说过的内容,Python 3.4引入了 contextlib.redirect_stdout上下文管理器。它接受要将输出重定向到的文件(类似)对象。

重定向到 /dev/null将会抑制输出:

In [11]: def f(): print('noise')


In [12]: import os, contextlib


In [13]: with open(os.devnull, 'w') as devnull:
....:     with contextlib.redirect_stdout(devnull):
....:         f()
....:


In [14]:

这个解决方案可以用作装饰:

import os, contextlib


def supress_stdout(func):
def wrapper(*a, **ka):
with open(os.devnull, 'w') as devnull:
with contextlib.redirect_stdout(devnull):
return func(*a, **ka)
return wrapper


@supress_stdout
def f():
print('noise')


f() # nothing is printed

在 Python 2和 Python 3中都可以使用的另一个可能且偶尔有用的解决方案是,将 /dev/null作为参数传递给 f,并使用 print函数的 file参数重定向输出:

In [14]: def f(target): print('noise', file=target)


In [15]: with open(os.devnull, 'w') as devnull:
....:     f(target=devnull)
....:


In [16]:

你甚至可以让 target完全可选:

def f(target=sys.stdout):
# Here goes the function definition

注意,你需要

from __future__ import print_function

在 Python 2中。

在很晚的时候才提到这个问题,我认为这是解决这个问题的一个更干净的方法。

import sys, traceback


class Suppressor(object):


def __enter__(self):
self.stdout = sys.stdout
sys.stdout = self


def __exit__(self, type, value, traceback):
sys.stdout = self.stdout
if type is not None:
# Do normal exception handling


def write(self, x): pass

用法:

with Suppressor():
DoMyFunction(*args,**kwargs)

自从 python 3.4以来,已经将 redirect _ stdout () 添加到 contextlib 中

对于 python > = 3.4,应该这样做:

import contextlib
import io


with contextlib.redirect_stdout(io.StringIO()):
foo()