Przekierowanie standardowego wyjścia do „nic” w Pythonie

128

Mam duży projekt składający się z dostatecznie dużej ilości modułów, każdy drukuje coś na standardowe wyjście. Teraz, gdy projekt się rozrósł, nie ma dużych. od printsprawozdań Drukowanie partii na std out, która tworzy program znacznie wolniej.

Tak więc chcę teraz zdecydować w czasie wykonywania, czy drukować cokolwiek na standardowe wyjście. Nie mogę wprowadzać zmian w modułach, ponieważ jest ich dużo. (Wiem, że mogę przekierować standardowe wyjście do pliku, ale nawet to jest znacznie powolne).

Więc moje pytanie brzmi: jak przekierować standardowe wyjście do niczego, tj. Jak sprawić, by printinstrukcja nic nie robiła?

# 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.

Obecnie jedynym pomysłem, jaki mam, jest utworzenie klasy, która ma metodę zapisu (która nic nie robi) i przekierowanie standardowego wyjścia do instancji tej klasy.

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

dp = DontPrint()
sys.stdout = dp

Czy jest do tego wbudowany mechanizm w Pythonie? A może jest coś lepszego niż to?

Pushpak Dagade
źródło
1
Właśnie odkryłem dziwną rzecz. Zmiana sys.stdout na None faktycznie działa w moim programie, chociaż nie jestem pewien dlaczego. Miałem naprawdę dziwne problemy z przekierowaniem kodowania do os.devnull, więc spróbowałem po prostu użyć None i działa. Może mieć coś wspólnego ze mną robiąc to w teście jednostkowym Django, ale nie jestem pewien.
Teekin

Odpowiedzi:

233

Wiele platform:

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

W systemie Windows:

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

W systemie Linux:

f = open('/dev/null', 'w')
sys.stdout = f
Andrew Clark
źródło
Jak to anulujesz? Czy istnieje sposób na ponowne włączenie instrukcji drukowania po zakończeniu?
jj172
4
Jasne, po prostu zapisz odniesienie do oryginału sys.stdouti odłóż je później. Na przykład old_stdout = sys.stdoutprzed jakimkolwiek innym kodem, a następnie sys.stdout = old_stdoutpo zakończeniu .
Andrew Clark
1
Uwaga: to nie przekierowuje na IE poziomu deskryptor pliku, może to nie przekierować wyjście zewnętrznych procesów potomnych, wyjście z rozszerzeniami C że druk do C stdout bezpośrednio os.write(1, b'abc\n'). Musisz os.dup2(), aby przekierować na poziomie deskryptora pliku, zobaczstdout_redirected()
jfs
4
Uwaga: służy sys.__stdout__do anulowania przekierowania. Więc po prostu to robisz sys.stdout = sys.__stdout__i nie musisz przechowywać kopii zapasowej.
Konstantin Sekeresh
28

Dobrym sposobem na zrobienie tego jest utworzenie małego procesora kontekstu, w który zawijasz swoje wydruki. Następnie po prostu użyj withinstrukcji -statement, aby wyciszyć wszystkie wydruki.

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 ma wbudowany procesor kontekstu, taki jak ten, więc możesz po prostu użyć biblioteki kontekstowej w następujący sposób:

import contextlib

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

print("this will print")

Uruchomienie tego kodu drukuje tylko drugą linię wyniku, a nie pierwszą:

$ python test.py
this will print

Działa to na wielu platformach (Windows + Linux + Mac OSX) i jest czystsze niż te inne, które odpowiadają imho.

Emil Stenström
źródło
@celticminstrel, masz całkowitą rację, dzięki za poprawienie mnie. Zaktualizowałem kod, aby później zresetować sys.stdout.
Emil Stenström
Brakuje sprawdzania błędów, ale świetny pomysł
Mad Physicist
Dodano kod pokazujący, jak to działa również w Pythonie 3.
Emil Stenström
15

Jeśli korzystasz z Pythona 3.4 lub nowszego, istnieje proste i bezpieczne rozwiązanie korzystające z biblioteki standardowej:

import contextlib

with contextlib.redirect_stdout(None):
  print("This won't print!")
iFreilicht
źródło
3
with contextlib.redirect_stdout(None)działa również
Chris Cogdon,
@ChrisCogdon Bardzo dobra wskazówka, odpowiednio zredagowałem odpowiedź.
iFreilicht
1
Nie działa, jeśli używasz biblioteki pprint. AttributeError: 'NoneType' object has no attribute 'write'
Speeeddy,
@Speeeddy czy dostałeś ten błąd w Pythonie 2? Możesz utworzyć klasę za pomocą fikcyjnej writemetody, która nic nie robi. Zobacz moją odpowiedź poniżej.
dizcza
8

(przynajmniej w moim systemie) wygląda na to, że zapis do os.devnull jest około 5x szybszy niż zapis do klasy DontPrint, tj.

#!/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)

dał następujący wynik:

<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
źródło
4
Tak dla przypomnienia
Antoine Pelisse
6

Jeśli pracujesz w środowisku Unix (w tym Linux), możesz przekierować dane wyjściowe do /dev/null:

python myprogram.py > /dev/null

A dla Windows:

python myprogram.py > nul
Nick Radford
źródło
2
Lepiej mieć rozwiązanie, które działałoby na wszystkich platformach.
Pushpak Dagade
2
@Guanidene, być może, ale jeśli jest jedyną osobą używającą jego programu, może w zasadzie zagwarantować środowisko.
Nick Radford,
nul = myfunc() ?
Hicsy
2

Co powiesz na to:

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()

Używa funkcji modułu contextlib do ukrycia danych wyjściowych dowolnego polecenia, które próbujesz uruchomić, w zależności od wyniku should_hide_output(), a następnie przywraca zachowanie wyjścia po wykonaniu tej funkcji.

Jeśli chcesz ukryć standardowe wyjście błędów, zaimportuj redirect_stderrz contextlibi dodaj wiersz z informacją stack.enter_context(redirect_stderr(null_stream)).

Głównym minusem jest to, że działa to tylko w Pythonie 3.4 i nowszych wersjach.

Elias Zamaria
źródło
1

Twoja klasa będzie działać dobrze (z wyjątkiem write()nazwy metody - należy ją wywołać write()małymi literami). Tylko pamiętaj, aby zapisać kopię sys.stdoutw innej zmiennej.

Jeśli korzystasz z * NIX, możesz to zrobić sys.stdout = open('/dev/null'), ale jest to mniej przenośne niż toczenie własnej klasy.

Rafe Kettler
źródło
1

Możesz po prostu kpić.

import mock

sys.stdout = mock.MagicMock()
Karthik Raja
źródło
0

Dlaczego tego nie spróbujesz?

sys.stdout.close()
sys.stderr.close()
Veerendra Kakumanu
źródło
Ponieważ A) to może nawet nie działać, a B) jeśli tak, otrzymasz błąd zamiast po prostu tłumić dane wyjściowe.
Mad Physicist
0
sys.stdout = None

To jest w porządku w print()przypadku. Ale może to spowodować błąd, jeśli wywołasz jakąkolwiek metodę sys.stdout, np sys.stdout.write().

W dokumentach znajduje się uwaga :

W pewnych warunkach stdin, stdout i stderr, a także oryginalne wartości stdin , stdout i stderr mogą mieć wartość None. Zwykle dzieje się tak w przypadku aplikacji GUI systemu Windows, które nie są połączone z konsolą, i aplikacji Python uruchamianych za pomocą pythonw.

Alexander Chzhen
źródło
„OK” i „Nie zgłosi błędu w momencie próby drukowania” to w tym przypadku bardzo różne rzeczy.
Mad Physicist,
1
Masz rację. Nie możesz wywołać żadnej metody, sys.stdout = Nullwięc w niektórych przypadkach może to być niebezpieczne.
Alexander Chzhen,
W tym przypadku wiadomo, że jest niebezpieczny. OP w szczególności odnosi się do błędu, jaki otrzymuje w wyniku tego.
Mad Physicist
0

Uzupełnienie odpowiedzi iFreilicht - działa zarówno dla Pythona 2, jak i 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!")
diżcza
źródło