Usar declaraciones de impresión solo para depurar

109

He estado codificando mucho en Python últimamente. Y he estado trabajando con datos con los que no había trabajado antes, usando fórmulas nunca antes vistas y tratando con archivos enormes. Todo esto me hizo escribir muchas declaraciones impresas para verificar si todo va bien e identificar los puntos de falla. Pero, en general, generar tanta información no es una buena práctica. ¿Cómo utilizo las declaraciones de impresión solo cuando quiero depurar y permitir que se omitan cuando no quiero que se impriman?

loco
fuente

Respuestas:

161

El loggingmódulo tiene todo lo que pueda desear. Puede parecer excesivo al principio, pero solo use las piezas que necesita. Me gustaría recomendar el uso logging.basicConfigpara cambiar el nivel de registro stderry los métodos de registro sencilla , debug, info, warning, errory critical.

import logging, sys
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
logging.debug('A debug message!')
logging.info('We processed %d records', len(processed_records))
Matt Joiner
fuente
5
Además, en caso de que haya tenido problemas para instalar este módulo como yo; el registro es parte de la biblioteca estándar, no es necesario instalar pip incluso cuando se usa un entorno virtual
Amr
¿Cómo se establece el nivel de registro de modo que solo imprima errores y no depure mensajes?
Eduardo Pignatelli
@EduardoPignatelli puso level, en la basicConfigllamada, a logging.ERROR.
Matt Joiner
Me temo que esto no funciona en jupyter lab 1.2.6. Puede configurar el nivel de registro una vez, y el restablecimiento que esté utilizando logging.basicConfig(stream=sys.stderr, level=logging.ERROR)no tendrá ningún efecto. Reiniciar el kernel y configurar el nuevo nivel funciona, pero esa es una solución para mí.
Eduardo Pignatelli
@EduardoPignatelli deberías hacer otra pregunta para esto. Pero es probable que deba cambiar directamente el nivel en el registrador raíz, es probable que jupyter esté llamando a basicConfig antes que usted.
Matt Joiner
28

Una forma sencilla de hacer esto es llamar a una función de registro:

DEBUG = True

def log(s):
    if DEBUG:
        print s

log("hello world")

Luego, puede cambiar el valor de DEBUGy ejecutar su código con o sin registro.

El loggingmódulo estándar tiene un mecanismo más elaborado para esto.

Greg Hewgill
fuente
5
Probablemente sea mejor a largo plazo usar el módulo de registro suministrado que rodar el suyo propio (aunque parece más complicado).
mgiuca
11
Es cierto, pero vale la pena comprender cómo se pueden hacer los suyos.
Greg Hewgill
1
En efecto. Lo anterior es una buena idea de cómo loggingfunciona (a un nivel muy simple).
mgiuca
Este es el que utilizo para mis aws lambdas.
crsuarezf
21

Utilice el módulo de biblioteca integrado de registro en lugar de imprimir.

Creas un Loggerobjeto (por ejemplo logger), y luego, cada vez que insertas una impresión de depuración, simplemente pones:

logger.debug("Some string")

Puede utilizar logger.setLevelal inicio del programa para establecer el nivel de salida. Si lo configura en DEBUG, imprimirá todas las depuraciones. Configúrelo en INFO o superior e inmediatamente todas las depuraciones desaparecerán.

También puedes usarlo para registrar cosas más serias, en diferentes niveles (INFO, ADVERTENCIA y ERROR).

mgiuca
fuente
12

En primer lugar, respaldaré la nominación del marco de registro de Python . Sin embargo, tenga un poco de cuidado con la forma en que lo usa. Específicamente: deje que el marco de registro amplíe sus variables, no lo haga usted mismo. Por ejemplo, en lugar de:

logging.debug("datastructure: %r" % complex_dict_structure)

asegúrese de hacer:

logging.debug("datastructure: %r", complex_dict_structure)

porque aunque se ven similares, la primera versión incurre en el costo repr () incluso si está deshabilitada . La segunda versión evita esto. Del mismo modo, si lanza el suyo, sugeriría algo como:

def debug_stdout(sfunc):
    print(sfunc())

debug = debug_stdout

llamado a través de:

debug(lambda: "datastructure: %r" % complex_dict_structure)

que, de nuevo, evitará la sobrecarga si la desactiva haciendo:

def debug_noop(*args, **kwargs):
    pass

debug = debug_noop

La sobrecarga de calcular esas cadenas probablemente no importe a menos que sean 1) costosas de calcular o 2) la declaración de depuración esté en medio de, digamos, un ciclo n ^ 3 o algo así. No es que yo sepa nada de eso.

pjz
fuente
Hay más información sobre este tema importante en 'optimización' en el cómo de
Martin CR
7

No sé sobre otros, pero estaba acostumbrado a definir una "constante global" ( DEBUG) y luego una función global ( debug(msg)) que se imprimiría msgsolo si DEBUG == True.

Luego escribo mis declaraciones de depuración como:

debug('My value: %d' % value)

... luego retomé las pruebas unitarias y ¡nunca volví a hacer esto! :)

Mac
fuente
Unidad de prueba ha. De acuerdo, eso es otra cosa que hay que recoger entonces :(
crazyaboutliv
1
No quiero desalentar las pruebas unitarias, es esencial. Pero no creo que sea un sustituto del registro, ni siquiera como técnica de depuración. Todavía imprimo mucho para probar cosas rápidamente.
mgiuca
@crazyaboutliv: las pruebas unitarias realizadas correctamente son excelentes. Eche un vistazo a este capítulo de sumergirse en Python para una presentación ágil, concisa y fácil de seguir
mac
@mgiuca - También hago impresiones rápidas, pero es solo un par de veces mientras llevo print()mi código al nivel requerido para pasar la prueba. Nunca termino con una gran cantidad de print()cosas por todos lados. ¡La tala también es genial! :)
mac
2
@mac Parece que su enlace ahora requiere un 'www' explícito; ahora está alojado aquí .
culix