在Python中静音一个函数的标准输出,而不是废弃sys.stdout并恢复每个函数调用

有没有办法在Python沉默标准输出没有包装像以下函数调用?

原始的破碎的代码:

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 

这种方式的工作,但似乎是非常低效。 必须有更好的方法。 有任何想法吗?

为你指定stdoutvariables没有任何作用,假设foo包含print语句 – 为什么你不应该从模块内部导入东西的另一个例子(就像你在这里做的那样),但总是作为一个模块整个(然后使用合格的名称)。 顺便说一下, 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() 

更多的优化:你只需要用一个没有操作write方法的对象replacesys.stdout即可。 例如:

 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实现相同的方式使用。 我不认为它比这更清洁或更快;-)。

为什么你认为这是低效的? 你testing了吗? 顺便说一句,它根本不起作用,因为你正在使用from ... import语句。 replacesys.stdout是好的,但不要复制,也不要使用临时文件。 改为打开空设备:

 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 

这个问题很晚才解决,我认为这是一个更清洁的解决scheme。

 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引入了contextlib.redirect_stdout上下文pipe理器。 它接受输出将被redirect到的文件(类)对象。

redirect到/ 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]: 

这个解决scheme可以适应作为装饰者使用:

 import os, contextlib def supress_stdout(func): def wrapper(): with open(os.devnull, 'w') as devnull: with contextlib.redirect_stdout(devnull): func() return wrapper @supress_stdout def f(): print('noise') f() # nothing is printed 

在Python 2和Python 3中都可以使用的另一种可能的偶尔有用的解决scheme是将/ dev / null作为parameter passing给f并使用print函数的file参数redirect输出:

 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中。

对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 

哪些得到我最初想要的,通常压制输出,但显示错误被抛出时抑制输出。