Stdout in Python auf "nichts" umleiten

128

Ich habe ein großes Projekt, das aus einer ausreichend großen Anzahl von Modulen besteht, von denen jedes etwas auf die Standardausgabe druckt. Jetzt, da das Projekt größer geworden ist, gibt es große Nr. von printAussagen, die viel auf den Standard drucken, was das Programm erheblich langsamer gemacht hat.

Daher möchte ich jetzt zur Laufzeit entscheiden, ob etwas auf dem Standard gedruckt werden soll oder nicht. Ich kann keine Änderungen an den Modulen vornehmen, da es viele davon gibt. (Ich weiß, dass ich den Standard in eine Datei umleiten kann, aber selbst das ist sehr langsam.)

Meine Frage ist also, wie ich den Standard zu nichts umleiten kann, dh wie kann ich die printAnweisung dazu bringen, nichts zu tun?

# I want to do something like this.
sys.stdout = None         # this obviously will give an error as Nonetype object does not have any write method.

Derzeit ist die einzige Idee, die ich habe, eine Klasse zu erstellen, die eine Schreibmethode hat (die nichts tut) und die Standardausgabe auf eine Instanz dieser Klasse umzuleiten.

class DontPrint(object):
    def write(*args): pass

dp = DontPrint()
sys.stdout = dp

Gibt es dafür einen eingebauten Mechanismus in Python? Oder gibt es etwas Besseres?

Pushpak Dagade
quelle
1
Ich habe gerade etwas Besonderes entdeckt. Das Verwandeln von sys.stdout in None funktioniert in meinem Programm tatsächlich, obwohl ich nicht sicher bin, warum. Ich hatte wirklich seltsame Probleme mit der Codierungsumleitung zu os.devnull, also habe ich versucht, einfach None zu verwenden, und es funktioniert. Könnte etwas damit zu tun haben, dass ich es in einem Django-Unit-Test mache, aber ich bin mir nicht sicher.
Teekin

Antworten:

233

Plattformübergreifend:

import os
import sys
f = open(os.devnull, 'w')
sys.stdout = f

Unter Windows:

f = open('nul', 'w')
sys.stdout = f

Unter Linux:

f = open('/dev/null', 'w')
sys.stdout = f
Andrew Clark
quelle
Wie stornierst du es dann? Gibt es eine Möglichkeit, Druckanweisungen wieder einzuschalten, nachdem Sie fertig sind?
JJ172
4
Sicher, speichern Sie einfach einen Verweis auf das Original sys.stdoutund setzen Sie ihn danach zurück. Zum Beispiel old_stdout = sys.stdoutvor einem der anderen Codes, sys.stdout = old_stdoutwenn Sie fertig sind.
Andrew Clark
1
Hinweis: Es wird nicht auf Dateideskriptorebene umgeleitet, dh es kann sein, dass die Ausgabe externer untergeordneter Prozesse, die Ausgabe von C-Erweiterungen, die direkt an C stdout gedruckt werden, nicht umgeleitet wird os.write(1, b'abc\n'). Sie müssen os.dup2(), um auf der Ebene des Dateideskriptors umzuleiten, siehestdout_redirected()
jfs
4
Beachten Sie, dass sys.__stdout__eine Umleitung abgebrochen werden soll. Sie müssen also nur sys.stdout = sys.__stdout__das Backup speichern.
Konstantin Sekeresh
28

Eine gute Möglichkeit, dies zu tun, besteht darin, einen kleinen Kontextprozessor zu erstellen, in den Sie Ihre Ausdrucke einwickeln. Sie verwenden dann nur is-in-a- withAnweisung, um alle Ausgaben stumm zu schalten.

Python 2:

import os
import sys
from contextlib import contextmanager

@contextmanager
def silence_stdout():
    old_target = sys.stdout
    try:
        with open(os.devnull, "w") as new_target:
            sys.stdout = new_target
            yield new_target
    finally:
        sys.stdout = old_target

with silence_stdout():
    print("will not print")

print("this will print")

Python 3.4+:

Python 3.4 verfügt über einen Kontextprozessor wie diesen, sodass Sie die Kontextlib einfach wie folgt verwenden können:

import contextlib

with contextlib.redirect_stdout(None):
    print("will not print")

print("this will print")

Wenn Sie diesen Code ausführen, wird nur die zweite Ausgabezeile gedruckt, nicht die erste:

$ python test.py
this will print

Dies funktioniert plattformübergreifend (Windows + Linux + Mac OSX) und ist sauberer als die anderen Antworten imho.

Emil Stenström
quelle
@ Celticminstrel Du hast vollkommen recht, danke, dass du mich korrigiert hast. Ich habe den Code aktualisiert, um sys.stdout anschließend zurückzusetzen.
Emil Stenström
Dies fehlt einige Fehlerprüfung, aber tolle Idee
Mad Physicist
Code hinzugefügt, um zu zeigen, wie dies auch in Python 3 funktioniert.
Emil Stenström
15

Wenn Sie in Python 3.4 oder höher arbeiten, gibt es eine einfache und sichere Lösung mit der Standardbibliothek:

import contextlib

with contextlib.redirect_stdout(None):
  print("This won't print!")
iFreilicht
quelle
3
with contextlib.redirect_stdout(None)funktioniert auch
Chris Cogdon
@ ChrisCogdon Sehr guter Hinweis, ich habe die Antwort entsprechend bearbeitet.
iFreilicht
1
Funktioniert nicht, wenn Sie eine Druckbibliothek verwenden. AttributeError: 'NoneType' object has no attribute 'write'
Speeeddy
@Speeeddy hast du diesen Fehler mit Python 2 bekommen? Sie können eine Klasse mit einer Dummy- writeMethode erstellen , die nichts bewirkt. Siehe meine Antwort unten.
Dizcza
8

(zumindest auf meinem System) Es scheint, dass das Schreiben in os.devnull etwa fünfmal schneller ist als das Schreiben in eine DontPrint-Klasse, d. h

#!/usr/bin/python
import os
import sys
import datetime

ITER = 10000000
def printlots(out, it, st="abcdefghijklmnopqrstuvwxyz1234567890"):
   temp = sys.stdout
   sys.stdout = out
   i = 0
   start_t = datetime.datetime.now()
   while i < it:
      print st
      i = i+1
   end_t = datetime.datetime.now()
   sys.stdout = temp
   print out, "\n   took", end_t - start_t, "for", it, "iterations"

class devnull():
   def write(*args):
      pass


printlots(open(os.devnull, 'wb'), ITER)
printlots(devnull(), ITER)

gab die folgende Ausgabe:

<open file '/dev/null', mode 'wb' at 0x7f2b747044b0> 
   took 0:00:02.074853 for 10000000 iterations
<__main__.devnull instance at 0x7f2b746bae18> 
   took 0:00:09.933056 for 10000000 iterations
DonP
quelle
4
Nur zur Veranschaulichung
Antoine Pelisse
6

Wenn Sie sich in einer Unix-Umgebung befinden (einschließlich Linux), können Sie die Ausgabe umleiten an /dev/null:

python myprogram.py > /dev/null

Und für Windows:

python myprogram.py > nul
Nick Radford
quelle
2
Es ist besser, eine Lösung zu haben, die auf allen Plattformen funktioniert.
Pushpak Dagade
2
@Guanidene vielleicht, aber wenn er der einzige ist, der sein Programm verwendet, kann er so ziemlich die Umwelt garantieren.
Nick Radford
nul = myfunc() ?
Hicsy
2

Wie wäre es damit:

from contextlib import ExitStack, redirect_stdout
import os

with ExitStack() as stack:
    if should_hide_output():
        null_stream = open(os.devnull, "w")
        stack.enter_context(null_stream)
        stack.enter_context(redirect_stdout(null_stream))
    noisy_function()

Dadurch werden die Funktionen im contextlib- Modul verwendet, um die Ausgabe des Befehls, den Sie ausführen möchten, abhängig vom Ergebnis von auszublenden und das Ausgabeverhalten should_hide_output()wiederherzustellen, nachdem diese Funktion ausgeführt wurde.

Wenn Sie die Standardfehlerausgabe ausblenden möchten, importieren Sie redirect_stderraus contextlibund fügen Sie eine Zeile mit der Aufschrift hinzu stack.enter_context(redirect_stderr(null_stream)).

Der Hauptnachteil ist, dass dies nur in Python 3.4 und späteren Versionen funktioniert.

Elias Zamaria
quelle
1

Ihre Klasse funktioniert einwandfrei (mit Ausnahme des write()Methodennamens - er muss in write()Kleinbuchstaben aufgerufen werden). Stellen Sie einfach sicher, dass Sie eine Kopie von sys.stdoutin einer anderen Variablen speichern .

Wenn Sie mit einem * NIX arbeiten, können Sie dies tun sys.stdout = open('/dev/null'), dies ist jedoch weniger portabel als das Rollen Ihrer eigenen Klasse.

Rafe Kettler
quelle
1

Sie können es einfach verspotten.

import mock

sys.stdout = mock.MagicMock()
Karthik Raja
quelle
0

Warum versuchst du das nicht?

sys.stdout.close()
sys.stderr.close()
Veerendra Kakumanu
quelle
Weil A) es möglicherweise nicht einmal funktioniert und B) wenn dies der Fall ist, wird ein Fehler angezeigt, anstatt nur die Ausgabe zu unterdrücken.
Verrückter Physiker
0
sys.stdout = None

Es ist OK für den print()Fall. Es kann jedoch einen Fehler verursachen, wenn Sie eine Methode von sys.stdout aufrufen, z sys.stdout.write().

In den Dokumenten ist ein Hinweis enthalten :

Unter bestimmten Bedingungen können stdin, stdout und stderr sowie die ursprünglichen Werte stdin , stdout und stderr None sein. Dies ist normalerweise bei Windows-GUI-Apps der Fall, die nicht mit einer Konsole verbunden sind, und bei Python-Apps, die mit Pythonw gestartet wurden.

Alexander Chzhen
quelle
"OK" und "Wird in dem Moment, in dem Sie versuchen zu drucken, keinen Fehler auslösen" sind in diesem Fall sehr unterschiedliche Dinge.
Mad Physicist
1
Du hast recht. Sie können keine Methode aufrufen, daher kann sys.stdout = Nulldies in einigen Fällen gefährlich sein.
Alexander Chzhen
In genau diesem Fall ist es als gefährlich bekannt. OP geht speziell auf den Fehler ein, den er dadurch bekommt.
Mad Physicist
0

Ergänzung zur Antwort von iFreilicht - es funktioniert sowohl für Python 2 als auch für Python 3.

import sys

class NonWritable:
    def write(self, *args, **kwargs):
        pass

class StdoutIgnore:
    def __enter__(self):
        self.stdout_saved = sys.stdout
        sys.stdout = NonWritable()
        return self

    def __exit__(self, *args):
        sys.stdout = self.stdout_saved

with StdoutIgnore():
    print("This won't print!")
Dizcza
quelle