Reindirizzamento di stdout a "niente" in Python


128

Ho un grande progetto costituito da un numero sufficientemente elevato di moduli, ognuno dei quali stampa qualcosa sullo standard output. Ora che il progetto è cresciuto in dimensioni, ci sono grandi no. di printistruzioni che stampano molto su STD, il che ha notevolmente rallentato il programma.

Quindi, ora voglio decidere in fase di esecuzione se stampare o meno qualcosa sullo stdout. Non posso apportare modifiche ai moduli in quanto ce ne sono molti. (So ​​di poter reindirizzare lo stdout su un file ma anche questo è considerevolmente lento.)

Quindi la mia domanda è: come reindirizzare lo stdout a niente, cioè come faccio a fare in modo che la printdichiarazione non faccia nulla?

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

Attualmente l'unica idea che ho è quella di creare una classe che abbia un metodo di scrittura (che non fa nulla) e reindirizzare lo stdout a un'istanza di questa classe.

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

dp = DontPrint()
sys.stdout = dp

Esiste un meccanismo integrato in Python per questo? O c'è qualcosa di meglio di questo?



1
Ho appena scoperto una cosa peculiare. Trasformare sys.stdout in None funziona davvero nel mio programma, anche se non sono sicuro del perché. Avevo problemi davvero strani con il reindirizzamento della codifica su os.devnull, quindi ho provato a usare None e funziona. Potrebbe avere qualcosa a che fare con me mentre lo faccio in un test unitario di Django, ma non ne sono sicuro.
Teekin,

Risposte:


233

Cross-platform:

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

Su Windows:

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

Su Linux:

f = open('/dev/null', 'w')
sys.stdout = f

Come lo annulli? C'è un modo per riattivare le dichiarazioni di stampa dopo aver finito?
jj172,

4
Certo, basta salvare un riferimento all'originale sys.stdoute ripristinarlo dopo. Ad esempio old_stdout = sys.stdoutprima di qualsiasi altro codice, quindi sys.stdout = old_stdoutquando hai finito.
Andrew Clark,

1
nota: non reindirizza ad un esempio livello di descrittore di file, potrebbe non riuscire a reindirizzare l'output di processi figli esterni, l'uscita dalle estensioni C che stampa a C stdout direttamente os.write(1, b'abc\n'). È necessario os.dup2(), per reindirizzare a livello di descrittore di file, vederestdout_redirected()
jfs il

4
Nota che c'è sys.__stdout__per annullare un reindirizzamento. Quindi lo fai sys.stdout = sys.__stdout__e non è necessario archiviare il backup.
Konstantin Sekeresh,

28

Un buon modo per farlo è quello di creare un piccolo processore di contesto in cui avvolgere le stampe. Basta quindi utilizzarlo in una withdichiarazione per mettere a tacere tutto l'output.

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 ha un processore di contesto come questo integrato, quindi puoi semplicemente usare il contesto in questo modo:

import contextlib

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

print("this will print")

L'esecuzione di questo codice stampa solo la seconda riga di output, non la prima:

$ python test.py
this will print

Funziona su più piattaforme (Windows + Linux + Mac OSX) ed è più pulito delle altre risposte imho.


@celticminstrel hai perfettamente ragione, grazie per avermi corretto. Successivamente ho aggiornato il codice per ripristinare sys.stdout.
Emil Stenström,

Manca un po 'di controllo degli errori, ma ottima idea
Mad Physicist,

Aggiunto codice per mostrare come funziona anche in Python 3.
Emil Stenström,

15

Se usi Python 3.4 o versioni successive, esiste una soluzione semplice e sicura che utilizza la libreria standard:

import contextlib

with contextlib.redirect_stdout(None):
  print("This won't print!")

3
with contextlib.redirect_stdout(None)funziona anche
Chris Cogdon,

@ChrisCogdon Ottimo suggerimento, ho modificato la risposta di conseguenza.
iFreilicht,

1
Non funziona se si utilizza la libreria pprint. AttributeError: 'NoneType' object has no attribute 'write'
Speeeddy,

@Speeeddy hai ricevuto questo errore con Python 2? Puoi creare una classe con un writemetodo fittizio che non fa nulla. Vedi la mia risposta qui sotto.
dizcza,

8

(almeno sul mio sistema) sembra che scrivere su os.devnull sia circa 5 volte più veloce rispetto a scrivere su una classe DontPrint, ovvero

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

ha dato il seguente risultato:

<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

4
Per la cronaca, la prossima volta che vorresti cronometrare
Antoine Pelisse

6

Se ti trovi in ​​un ambiente Unix (incluso Linux), puoi reindirizzare l'output a /dev/null:

python myprogram.py > /dev/null

E per Windows:

python myprogram.py > nul

2
È meglio avere una soluzione che funzioni su tutte le piattaforme.
Pushpak Dagade,

2
@Guanidene, forse, ma se è l'unica persona che usa il suo programma, può praticamente garantire l'ambiente.
Nick Radford,

nul = myfunc() ?
Hicsy,

2

Cosa ne pensi di questo:

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

Questo utilizza le funzionalità nel modulo contextlib per nascondere l'output di qualunque comando si stia eseguendo, a seconda del risultato dishould_hide_output() , e quindi ripristinare il comportamento dell'output dopo l'esecuzione di tale funzione.

Se si desidera nascondere standard error, quindi importare redirect_stderrda contextlibe aggiungere una riga dicendostack.enter_context(redirect_stderr(null_stream)) .

Il principale svantaggio è che funziona solo in Python 3.4 e versioni successive.



1

La tua classe funzionerà perfettamente (ad eccezione del write()nome del metodo - deve essere chiamata in write()minuscolo). Assicurati di salvare una copia disys.stdout in un'altra variabile.

Se sei su un * NIX, puoi farlo sys.stdout = open('/dev/null'), ma questo è meno portabile della tua stessa classe.


1

Puoi semplicemente deriderlo.

import mock

sys.stdout = mock.MagicMock()

0

Perché non provi questo?

sys.stdout.close()
sys.stderr.close()

Poiché A) potrebbe non funzionare nemmeno, e B) in caso affermativo, verrà visualizzato un errore anziché eliminare semplicemente l'output.
Fisico pazzo,

0
sys.stdout = None

Va bene per il print()caso. Ma può causare un errore se si chiama un metodo di sys.stdout, ad es sys.stdout.write().

C'è una nota nei documenti :

In alcune condizioni stdin, stdout e stderr nonché i valori originali stdin , stdout e stderr possono essere None. Di solito è il caso delle app GUI di Windows che non sono collegate a una console e le app Python sono state avviate con pythonw.


"OK" e "Non genererà un errore nel momento in cui si tenta di stampare" sono cose molto diverse in questo caso.
Fisico pazzo,

1
Hai ragione. Non è possibile chiamare alcun metodo con sys.stdout = Nullquindi in alcuni casi può essere pericoloso.
Alexander Chzhen,

In questo preciso caso è noto per essere pericoloso. OP affronta in modo specifico l'errore che ottiene dal farlo.
Fisico pazzo,

0

Supplemento alla risposta di iFreilicht : funziona sia con Python 2 che 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!")
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.