web-dev-qa-db-ja.com

sys.stdoutを破棄して各関数呼び出しを復元せずに、関数の標準出力をPythonで無音にします

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より良い方法があります。何か案は?

42
Tim McJilton

stdoutfooステートメントを含むと仮定すると、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の実装と同じ方法で使用されます。これよりもすっきりしたり速くなるとは思わない;-)。

80
Alex Martelli

他の人がすでに言ったことに追加するために、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。

19
vaultah

なぜこれが非効率だと思いますか? 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
15
Philipp

私がこの問題のよりクリーンな解決策だと思ったもので、これに非常に遅れて鳴り響きました。

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)
13
bitmous

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)
5
tgray

さらに一般化することにより、出力をキャプチャして返すことさえできる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
2
e-satis

これよりもクリーンで高速になるとは思わない;-)

ああ!私はもう少し良くできると思う:-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

これは、出力を正常に抑制するが、エラーがスローされた場合に抑制された出力を表示するために、私が当初望んでいたものに到達します。

2
PatB

redirect_stdout() は、python 3.4以降、contextlibに追加されました。

python> = 3.4の場合、これを行う必要があります。

import contextlib
import io

with contextlib.redirect_stdout(io.StringIO()):
    foo()
1
Pin-Yen