Redirigiendo stdout a "nada" en python

128

Tengo un gran proyecto que consta de un número suficientemente grande de módulos, cada uno imprimiendo algo en la salida estándar. Ahora que el proyecto ha crecido en tamaño, hay grandes no. de printdeclaraciones que imprimen mucho en la salida estándar lo que ha hecho que el programa sea considerablemente más lento.

Entonces, ahora quiero decidir en tiempo de ejecución si imprimir o no algo en el stdout. No puedo hacer cambios en los módulos, ya que hay muchos. (Sé que puedo redirigir el stdout a un archivo, pero incluso esto es considerablemente lento).

Entonces, mi pregunta es ¿cómo redirijo el stdout a nada, es decir, cómo hago que la printdeclaración no haga nada?

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

Actualmente, la única idea que tengo es crear una clase que tenga un método de escritura (que no hace nada) y redirigir el stdout a una instancia de esta clase.

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

dp = DontPrint()
sys.stdout = dp

¿Hay un mecanismo incorporado en Python para esto? ¿O hay algo mejor que esto?

Pushpak Dagade
fuente
1
Acabo de descubrir una cosa peculiar. Convertir sys.stdout en None realmente funciona en mi programa, aunque no estoy seguro de por qué. Estaba teniendo problemas realmente extraños con la codificación que redirige a os.devnull, así que intenté usar None y funciona. Podría tener algo que ver conmigo haciéndolo en una prueba unitaria de Django, pero no estoy seguro.
Teekin

Respuestas:

233

Multiplataforma:

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

En Windows:

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

En Linux:

f = open('/dev/null', 'w')
sys.stdout = f
Andrew Clark
fuente
¿Cómo lo cancelas? ¿Hay alguna manera de volver a activar las declaraciones impresas después de que haya terminado?
jj172
44
Claro, solo guarde una referencia al original sys.stdouty vuelva a configurarlo después. Por ejemplo, old_stdout = sys.stdoutantes de cualquiera de los otros códigos, luego sys.stdout = old_stdoutcuando haya terminado.
Andrew Clark
1
nota: no redirige a nivel de descriptor de archivo, es decir, puede fallar al redirigir la salida de procesos secundarios externos, la salida de extensiones C que se imprimen directamente en C stdout os.write(1, b'abc\n'). Necesita os.dup2(), para redirigir en el nivel de descriptor de archivo, veastdout_redirected()
jfs
44
Tenga en cuenta que hay sys.__stdout__para cancelar una redirección. Así que solo debes hacerlo sys.stdout = sys.__stdout__y no es necesario almacenar la copia de seguridad.
Konstantin Sekeresh
28

Una buena manera de hacer esto es crear un pequeño procesador de contexto en el que se envuelvan sus impresiones. Luego, simplemente use una withdeclaración para silenciar todos los resultados.

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 tiene un procesador de contexto como este incorporado, por lo que simplemente puede usar contextlib de esta manera:

import contextlib

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

print("this will print")

La ejecución de este código solo imprime la segunda línea de salida, no la primera:

$ python test.py
this will print

Esto funciona multiplataforma (Windows + Linux + Mac OSX), y es más limpio que las otras respuestas en mi humilde opinión.

Emil Stenström
fuente
@celticminstrel tienes toda la razón, gracias por corregirme. He actualizado el código para restablecer sys.stdout después.
Emil Stenström
Faltan algunas comprobaciones de errores, pero es una gran idea
Mad Physicist
Código agregado para mostrar cómo funciona esto en Python 3 también.
Emil Stenström
15

Si está en Python 3.4 o superior, hay una solución simple y segura que utiliza la biblioteca estándar:

import contextlib

with contextlib.redirect_stdout(None):
  print("This won't print!")
iFreilicht
fuente
3
with contextlib.redirect_stdout(None)funciona bien
Chris Cogdon
@ChrisCogdon Muy buena pista, edité la respuesta en consecuencia.
iFreilicht
1
No funciona si está utilizando la biblioteca pprint. AttributeError: 'NoneType' object has no attribute 'write'
Speeeddy
@ Speeeddy, ¿recibiste este error con Python 2? Puede crear una clase con un writemétodo ficticio que no hace nada. Vea mi respuesta a continuación.
dizcza
8

(al menos en mi sistema) parece que escribir en os.devnull es aproximadamente 5 veces más rápido que escribir en una clase DontPrint, es decir

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

dio el siguiente resultado:

<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
fuente
44
Solo para que conste, la próxima vez que quieras cronometrar
Antoine Pelisse
6

Si está en un entorno Unix (Linux incluido), puede redirigir la salida a /dev/null:

python myprogram.py > /dev/null

Y para Windows:

python myprogram.py > nul
Nick Radford
fuente
2
Es mejor tener una solución que funcione en todas las plataformas.
Pushpak Dagade
2
@Guanidene, tal vez, pero si él es la única persona que usa su programa, puede garantizar el medio ambiente.
Nick Radford
nul = myfunc() ?
Hicsy
2

Qué tal esto:

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

Esto usa las características en el módulo contextlib para ocultar la salida de cualquier comando que esté intentando ejecutar, dependiendo del resultado deshould_hide_output() , y luego restaura el comportamiento de salida después de que esa función se haya ejecutado.

Si desea ocultar la salida de error estándar, a continuación, importar redirect_stderrdesde contextliby añadir una línea que digastack.enter_context(redirect_stderr(null_stream)) .

El principal inconveniente es que esto solo funciona en Python 3.4 y versiones posteriores.

Elias Zamaria
fuente
1

Su clase funcionará bien (con la excepción del write()nombre del método; debe llamarse en write()minúsculas). Solo asegúrese de guardar una copia de sys.stdouten otra variable.

Si está en un * NIX, puede hacerlo sys.stdout = open('/dev/null'), pero esto es menos portátil que rodar su propia clase.

Rafe Kettler
fuente
1

Puedes burlarte de eso.

import mock

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

¿Por qué no intentas esto?

sys.stdout.close()
sys.stderr.close()
Veerendra Kakumanu
fuente
Debido a que A) podría no funcionar, y B) si lo hace, obtendrá un error en lugar de simplemente suprimir la salida.
Físico loco
0
sys.stdout = None

Está bien para el print()caso. Pero puede causar un error si llama a cualquier método de sys.stdout, por ejemplo sys.stdout.write().

Hay una nota en los documentos :

En algunas condiciones, stdin, stdout y stderr, así como los valores originales, stdin , stdout y stderr pueden ser Ninguno. Por lo general, es el caso de las aplicaciones GUI de Windows que no están conectadas a una consola y las aplicaciones Python comenzaron con pythonw.

Alexander Chzhen
fuente
"Aceptar" y "No arrojará un error en el momento en que intente imprimir" son cosas muy diferentes en este caso.
Físico loco
1
Tienes razón. No puede llamar a ningún método, por sys.stdout = Nulllo que en algunos casos puede ser peligroso.
Alexander Chzhen
En este caso exacto se sabe que es peligroso. OP aborda específicamente el error que obtiene al hacerlo.
Físico loco
0

Complemento a la respuesta de iFreilicht : funciona tanto para python 2 como para 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
fuente