Usando instruções de impressão apenas para depurar

109

Ultimamente, tenho programado muito em Python. E tenho trabalhado com dados com os quais não trabalhei antes, usando fórmulas nunca vistas antes e lidando com arquivos enormes. Tudo isso me fez escrever várias declarações impressas para verificar se está tudo certo e identificar os pontos de falha. Mas, geralmente, emitir tantas informações não é uma boa prática. Como uso as instruções de impressão apenas quando quero depurar e deixo que sejam ignoradas quando não quero que sejam impressas?

crazyaboutliv
fonte

Respostas:

161

O loggingmódulo tem tudo o que você pode desejar. Pode parecer excessivo no início, mas use apenas as peças de que precisa. Eu recomendo usar logging.basicConfigpara alternar o nível de log para stderre os métodos de log simples , debug, info, warning, errore 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
fonte
5
Além disso, caso você tenha problemas para instalar este módulo como eu; o registro faz parte da biblioteca padrão - sem necessidade de instalação do pip mesmo ao usar um ambiente virtual
Amr
Como definir o nível de registro de modo que ele apenas imprima erros e não mensagens de depuração?
Eduardo Pignatelli
@EduardoPignatelli definido level, na basicConfigchamada, para logging.ERROR.
Matt Joiner
Temo que isso não funcione no jupyter lab 1.2.6. Você pode definir o nível de registro uma vez e redefinir o uso logging.basicConfig(stream=sys.stderr, level=logging.ERROR)não terá efeito. Reiniciar o kernel e definir o novo nível funciona, mas isso é uma solução alternativa para mim.
Eduardo Pignatelli
@EduardoPignatelli você deveria fazer outra pergunta para isso. Mas provavelmente você precisará alterar diretamente o nível no logger root, jupyter provavelmente está chamando basicConfig antes de você.
Matt Joiner
28

Uma maneira simples de fazer isso é chamar uma função de registro:

DEBUG = True

def log(s):
    if DEBUG:
        print s

log("hello world")

Em seguida, você pode alterar o valor de DEBUGe executar seu código com ou sem registro.

O loggingmódulo padrão possui um mecanismo mais elaborado para isso.

Greg Hewgill
fonte
5
Provavelmente, a longo prazo, é melhor usar o módulo de registro fornecido do que fazer o seu próprio (embora pareça mais complicado).
mgiuca
11
É verdade, mas vale a pena entender como se pode rolar por conta própria.
Greg Hewgill
1
De fato. O texto acima é uma boa ideia de como loggingfunciona (em um nível muito simples).
mgiuca
Este é o que eu uso para meus lambdas aws.
crsuarezf
21

Use o módulo de biblioteca integrado de registro em vez de imprimir.

Você cria um Loggerobjeto (digamos logger) e, depois disso, sempre que inserir uma impressão de depuração, basta colocar:

logger.debug("Some string")

Você pode usar logger.setLevelno início do programa para definir o nível de saída. Se você configurá-lo para DEBUG, ele imprimirá todas as depurações. Defina-o como INFO ou superior e imediatamente todas as depurações desaparecerão.

Você também pode usá-lo para registrar coisas mais sérias, em diferentes níveis (INFO, WARNING e ERROR).

mgiuca
fonte
12

Em primeiro lugar, vou apoiar a nomeação do framework de registro de python . No entanto, tenha um pouco de cuidado ao usá-lo. Especificamente: deixe a estrutura de registro expandir suas variáveis, não faça você mesmo. Por exemplo, em vez de:

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

certifique-se de fazer:

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

porque embora pareçam semelhantes, a primeira versão incorre no custo repr (), mesmo se estiver desabilitada . A segunda versão evita isso. Da mesma forma, se você lançar o seu próprio, sugiro algo como:

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

debug = debug_stdout

chamado via:

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

o que, novamente, evitará a sobrecarga se você desativá-lo fazendo:

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

debug = debug_noop

A sobrecarga de computação dessas strings provavelmente não importa, a menos que sejam 1) caras de calcular ou 2) a instrução de depuração esteja no meio de, digamos, um loop n ^ 3 ou algo assim. Não que eu soubesse algo sobre isso.

pjz
fonte
Há mais informações sobre este tópico importante em 'otimização' no howto de registro: docs.python.org/3/howto/logging.html#optimization
Martin CR
7

Não sei sobre outros, mas estava acostumado a definir uma "constante global" ( DEBUG) e, em seguida, uma função global ( debug(msg)) que seria impressa msgapenas se DEBUG == True.

Em seguida, escrevo minhas instruções de depuração como:

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

... então pego o teste de unidade e nunca mais fiz isso! :)

Mac
fonte
Teste de unidade ha. Ok, isso é outra coisa a ser aprendida então :(
crazyaboutliv
1
Não quero desencorajar o teste de unidade - é essencial. Mas não acho que seja um substituto para o log, mesmo como uma técnica de depuração. Ainda imprimo muito para testar as coisas rapidamente.
mgiuca
@crazyaboutliv - O teste de unidade feito corretamente é ótimo. Dê uma olhada neste capítulo sobre como mergulhar em python para obter uma apresentação rápida, concisa e fácil de seguir
mac
@mgiuca - Também faço impressão rápida, mas print()leva apenas alguns minutos para colocar meu código no nível necessário para passar no teste. Eu nunca acabo com uma grande quantidade de print()por todo o lugar. Registrar também é legal! :)
mac
2
@mac Parece que seu link agora requer um 'www' explícito - agora ele está hospedado aqui .
culix