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
その方法は機能しますが、ひどく非効率的なようです。そこにhasより良い方法があります。何か案は?
stdout
がfoo
ステートメントを含むと仮定すると、print
変数を実行中に割り当てても何の効果もありません-モジュールからinside(ここで)、しかし常に全体としてモジュール(そして修飾名を使用します)。ちなみに、copy
は無関係です。スニペットの正しい同等物は次のとおりです。
import sys
save_stdout = sys.stdout
sys.stdout = open('trash', 'w')
foo()
sys.stdout = save_stdout
Nowは、コードが正しい場合に、コードをよりエレガントまたは高速にするときです。たとえば、ファイル 'trash'の代わりにメモリ内のファイルのようなオブジェクトを使用できます。
import sys
import io
save_stdout = sys.stdout
sys.stdout = io.BytesIO()
foo()
sys.stdout = save_stdout
優雅さのために、contextが最適です、例えば:
import contextlib
import io
import sys
@contextlib.contextmanager
def nostdout():
save_stdout = sys.stdout
sys.stdout = io.BytesIO()
yield
sys.stdout = save_stdout
このコンテキストを定義したら、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
コンテキストマネージャー。出力のリダイレクト先となるfile(-like)オブジェクトを受け入れます。
/ 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
in Python 2。
なぜこれが非効率だと思いますか? test it?ところで、from ... import
ステートメントを使用しているため、まったく機能しません。 sys.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
私がこの問題のよりクリーンな解決策だと思ったもので、これに非常に遅れて鳴り響きました。
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()
が何度も呼び出された場合、関数をラップする(装飾する)方が良い/簡単になる可能性があります。このようにして、with-statementで関数の使用をすべて囲むのではなく、foo
の定義を1回変更します。
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)
さらに一般化することにより、出力をキャプチャして返すことさえできるNiceデコレータを取得できます。
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
これは、出力を正常に抑制するが、エラーがスローされた場合に抑制された出力を表示するために、私が当初望んでいたものに到達します。
redirect_stdout() は、python 3.4以降、contextlibに追加されました。
python> = 3.4の場合、これを行う必要があります。
import contextlib
import io
with contextlib.redirect_stdout(io.StringIO()):
foo()