¿Cómo bloquear llamadas para imprimir?

81

¿Hay alguna forma de evitar que una función llame print?


Estoy usando el pygame.joystick módulo para un juego en el que estoy trabajando.

Creé un pygame.joystick.Joystickobjeto y en el bucle real del juego llamé a su función miembro get_buttonpara verificar la entrada del usuario. La función hace todo lo que necesito que haga, pero el problema es que también llama print, lo que ralentiza considerablemente el juego.

¿Puedo bloquear esta llamada a print?

dmlicht
fuente

Respuestas:

108

Python le permite sobrescribir la salida estándar (stdout) con cualquier objeto de archivo. Esto debería funcionar multiplataforma y escribir en el dispositivo nulo.

import sys, os

# Disable
def blockPrint():
    sys.stdout = open(os.devnull, 'w')

# Restore
def enablePrint():
    sys.stdout = sys.__stdout__


print 'This will print'

blockPrint()
print "This won't"

enablePrint()
print "This will too"

Si no desea que se imprima esa función, llámela blockPrint()antes y enablePrint()cuando desee que continúe. Si desea desactivar toda la impresión, comience a bloquear en la parte superior del archivo.

Bandido
fuente
22
Esto parece haberme bloqueado permanentemente la impresión. enablePrint no lo restaura
Johnny V
10
Esta solución no restaurará correctamente la impresión en las celdas de Jupyter
oski86
1
Supongo que el argumento para imprimir todavía se evalúa, por lo que tomará más tiempo que el script con todas las líneas de impresión comentadas manualmente.
Radio controlado
79

Basado en la solución @FakeRainBrigand, sugiero una solución más segura:

import os, sys

class HiddenPrints:
    def __enter__(self):
        self._original_stdout = sys.stdout
        sys.stdout = open(os.devnull, 'w')

    def __exit__(self, exc_type, exc_val, exc_tb):
        sys.stdout.close()
        sys.stdout = self._original_stdout

Entonces puedes usarlo así:

with HiddenPrints():
    print("This will not be printed")

print("This will be printed as before")

Esto es mucho más seguro porque no puede olvidarse de volver a habilitar stdout, que es especialmente crítico cuando se manejan excepciones.

Sin with

El siguiente ejemplo usa las funciones de habilitar / deshabilitar impresiones que se sugirieron en la respuesta anterior.

Imagine que hay un código que puede generar una excepción. Tuvimos que usar finallydeclaración para habilitar impresiones en cualquier caso.

try:
    disable_prints()
    something_throwing()
    enable_prints() # This will not help in case of exception
except ValueError as err:
    handle_error(err)
finally:
    enable_prints() # That's where it needs to go.

Si olvidó la finallycláusula, ninguna de sus printllamadas imprimiría nada más.

Es más seguro usar la withinstrucción, que asegura que las impresiones se volverán a habilitar.

Nota: No es seguro de usar sys.stdout = None, porque alguien podría llamar a métodos comosys.stdout.write()

Alexander Chzhen
fuente
6
Muy buena solucion! Simplemente escribí lo mismo y luego descubrí que ya respondiste de esa manera: D Agregaré un poco de información sobre por qué esta es una mejor manera de hacerlo.
iFreilicht
Pregunta de novato aquí: ¿Sería importante cerrar () devnull después de salir de la clase?
jlsecrest
@Wadsworth, no sé la respuesta. Supongo que las propiedades de devnull no requieren que se cierre correctamente. Pero los rumores dicen que siempre debes cerrar los controladores abiertos para liberar recursos. CPython, como sé, debería cerrar devnull en la medida en que el recolector de basura lo obtenga.
Alexander Chzhen
2
Estaba obteniendo ResourceWarning: unclosed file <_io.TextIOWrapper name='/dev/null' mode='w' encoding='UTF-8'>al usar este código, lo resolví configurando sys.stdout = None en lugar de open (os.devnull, 'w')
WellDone2094
@ WellDone2094, gracias. He agregado sys.stdout.close()al método de salida. Esto debería ayudar. Tenga en cuenta que sys.stdout = Nonepuede causar un error, porque alguien puede llamar a los métodos de stdout como sys.stdout.write().
Alexander Chzhen
33

Como sugirió @Alexander Chzhen, usar un administrador de contexto sería más seguro que llamar a un par de funciones de cambio de estado.

Sin embargo, no es necesario volver a implementar el administrador de contexto; ya está en la biblioteca estándar. Puede redirigir stdout(el objeto de archivo que printutiliza) con contextlib.redirect_stdout, y también stderrcon contextlib.redirect_stderr.

import os
import contextlib

with open(os.devnull, "w") as f, contextlib.redirect_stdout(f):
    print("This won't be printed.")
Paraguas olvidado
fuente
7

Si desea bloquear las llamadas de impresión realizadas por una función en particular, existe una solución más ordenada utilizando decoradores. Defina el siguiente decorador:

# decorater used to block function printing to the console
def blockPrinting(func):
    def func_wrapper(*args, **kwargs):
        # block all printing to the console
        sys.stdout = open(os.devnull, 'w')
        # call the method in question
        value = func(*args, **kwargs)
        # enable all printing to the console
        sys.stdout = sys.__stdout__
        # pass the return value of the method back
        return value

    return func_wrapper

Luego, colóquelo @blockPrintingantes de cualquier función. Por ejemplo:

# This will print
def helloWorld():
    print("Hello World!")
helloWorld()

# This will not print
@blockPrinting
def helloWorld2():
    print("Hello World!")
helloWorld2()
Cazador de aves
fuente
2

No, no lo hay, especialmente porque la mayoría de PyGame está escrito en C.

Pero si esta función llama a print, entonces es un error de PyGame, y debería informarlo.

Gato Plus Plus
fuente
2

Tuve el mismo problema y no encontré otra solución que la de redirigir la salida del programa (no sé exactamente si el spam ocurre en stdout o stderr) al /dev/nullnirvana.

De hecho, es de código abierto, pero no me apasionó lo suficiente como para sumergirme en las pygamefuentes, y el proceso de compilación, para detener de alguna manera el spam de depuración.

EDITAR:

El pygame.joystickmódulo tiene llamadas a printfen todas las funciones que devuelven los valores reales a Python:

printf("SDL_JoystickGetButton value:%d:\n", value);

Desafortunadamente, tendría que comentarlos y volver a compilar todo. Quizás lo provisto setup.pyharía esto más fácil de lo que pensaba. Podrías probar esto ...

moooeeeep
fuente
2

Un enfoque completamente diferente sería redirigir en la línea de comando. Si está en Windows, esto significa un script por lotes. En Linux, bash.

/full/path/to/my/game/game.py > /dev/null
C:\Full\Path\To\My\Game.exe > nul

A menos que esté tratando con varios procesos, esto debería funcionar. Para los usuarios de Windows, estos podrían ser los accesos directos que está creando (menú de inicio / escritorio).

Bandido
fuente
1

El módulo que usé imprimió stderr. Entonces la solución en ese caso sería:

sys.stdout = open(os.devnull, 'w')
David Schumann
fuente
1

Basado en la solución @Alexander Chzhen, presento aquí la forma de aplicarlo en una función con una opción para suprimir la impresión o no.

    import os, sys
    class SuppressPrints:
        #different from Alexander`s answer
        def __init__(self, suppress=True):
            self.suppress = suppress

        def __enter__(self):
            if self.suppress:
                self._original_stdout = sys.stdout
                sys.stdout = open(os.devnull, 'w')

        def __exit__(self, exc_type, exc_val, exc_tb):
            if self.suppress:
                sys.stdout.close()
                sys.stdout = self._original_stdout
    #implementation
    def foo(suppress=True):
        with SuppressPrints(suppress):
            print("It will be printed, or not")

    foo(True)  #it will not be printed
    foo(False) #it will be printed

Espero poder agregar mi solución debajo de la respuesta de Alexander como comentario, pero no tengo suficientes (50) reputaciones para hacerlo.

Quân Hoàng
fuente
0

Puede hacer una redirección simple, esto parece mucho más seguro que jugar con stdout y no incluye bibliotecas adicionales.

enable_print  = print
disable_print = lambda *x, **y: None

print = disable_print
function_that_has_print_in_it(1)  # nothing is printed

print = enable_print
function_that_has_print_in_it(2)  # printing works again!

Nota: esto solo funciona para deshabilitar la función print () y no deshabilitaría toda la salida si está haciendo llamadas a otra cosa que está produciendo salida. Por ejemplo, si estaba llamando a una biblioteca C que estaba produciendo su propia salida a stdout, o si estaba usando intput ().

xelf
fuente