有没有一种方法在Python中静音stdout而不包含像下面这样的函数调用?
原始破码:
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
这种方式有效,但似乎非常低效。一定有更好的方法。有任何想法吗?
正如你所做的那样,分配stdout
变量没有任何效果,假设foo
包含print
语句 - 这是为什么你永远不应该从模块内部导入东西的另一个例子(正如你在这里做的那样),但总是将模块作为一个模块整体(然后使用合格的名字)。顺便说一句,copy
无关紧要。您的代码段的正确等效项是:
import sys
save_stdout = sys.stdout
sys.stdout = open('trash', 'w')
foo()
sys.stdout = save_stdout
现在,当代码正确时,是时候让它更优雅或更快。例如,您可以使用内存中类文件对象而不是文件'trash':
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
实现方式相同。我不认为它比这更清洁或更快;-)。
为了补充其他人已经说过的内容,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):
func(*a, **ka)
return wrapper
@supress_stdout
def f():
print('noise')
f() # nothing is printed
在Python 2和3中都可以使用的另一种可能且偶尔有用的解决方案是将/ dev / null作为参数传递给f
,并使用file
函数的print
参数重定向输出:
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中。
为什么你认为这是低效的?你测试过吗?顺便说一句,它根本不起作用,因为你使用的是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
我觉得这个问题是一个更清洁的解决方案。
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)
对Alex Martelli's answer略有修改......
这解决了您总是想要为函数抑制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以来,redirect_stdout()已添加到contextlib中
对于python> = 3.4,这应该这样做:
import contextlib
import io
with contextlib.redirect_stdout(io.StringIO()):
foo()