Utilisation des instructions d'impression uniquement pour déboguer

109

J'ai beaucoup codé en Python ces derniers temps. Et j'ai travaillé avec des données avec lesquelles je n'avais jamais travaillé auparavant, en utilisant des formules jamais vues auparavant et en traitant d'énormes fichiers. Tout cela m'a fait écrire beaucoup de déclarations imprimées pour vérifier si tout va bien et identifier les points de défaillance. Mais, généralement, produire autant d'informations n'est pas une bonne pratique. Comment utiliser les instructions d'impression uniquement lorsque je veux déboguer et les laisser être ignorées lorsque je ne veux pas qu'elles soient imprimées?

crazyaboutliv
la source

Réponses:

161

Le loggingmodule a tout ce que vous pourriez souhaiter. Cela peut paraître excessif au début, mais n'utilisez que les pièces dont vous avez besoin. Je vous recommande d' utiliser logging.basicConfigpour activer le niveau d'enregistrement stderret les méthodes de journal simples , debug, info, warning, erroret 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
la source
5
Aussi, au cas où vous auriez du mal à installer ce module comme moi; la journalisation fait partie de la bibliothèque standard - pas besoin d'installer pip même lors de l'utilisation d'un environnement virtuel
Amr
Comment définir le niveau de journalisation de manière à n'imprimer que les erreurs et non les messages de débogage?
Eduardo Pignatelli
@EduardoPignatelli a défini level, dans l' basicConfigappel, sur logging.ERROR.
Matt Joiner
J'ai peur que cela ne fonctionne pas sur Jupyter Lab 1.2.6. Vous pouvez définir le niveau de journalisation une fois et la réinitialisation en utilisant logging.basicConfig(stream=sys.stderr, level=logging.ERROR)n'aura aucun effet. Redémarrer le noyau et définir le nouveau niveau fonctionne, mais c'est une solution de contournement pour moi.
Eduardo Pignatelli le
@EduardoPignatelli vous devriez poser une autre question à ce sujet. Mais vous devrez probablement changer directement le niveau de l'enregistreur racine, jupyter appelle probablement basicConfig avant vous.
Matt Joiner
28

Un moyen simple de le faire est d'appeler une fonction de journalisation:

DEBUG = True

def log(s):
    if DEBUG:
        print s

log("hello world")

Ensuite, vous pouvez modifier la valeur de DEBUGet exécuter votre code avec ou sans journalisation.

Le loggingmodule standard a un mécanisme plus élaboré pour cela.

Greg Hewgill
la source
5
Il est probablement préférable à long terme d'utiliser le module de journalisation fourni que de lancer le vôtre (même si cela semble plus compliqué).
mgiuca
11
C'est vrai, mais cela vaut la peine de comprendre comment on peut rouler le sien.
Greg Hewgill
1
En effet. Ce qui précède est une bonne idée de la façon dont loggingfonctionne (à un niveau très simple).
mgiuca
C'est celui que j'utilise pour mes lambdas aws.
crsuarezf
21

Utilisez le module de bibliothèque intégré de journalisation au lieu d'imprimer.

Vous créez un Loggerobjet (par exemple logger), puis après cela, chaque fois que vous insérez une impression de débogage, vous venez de mettre:

logger.debug("Some string")

Vous pouvez utiliser logger.setLevelau début du programme pour régler le niveau de sortie. Si vous le définissez sur DEBUG, il affichera tous les débogages. Réglez-le sur INFO ou supérieur et immédiatement tous les débogages disparaîtront.

Vous pouvez également l'utiliser pour enregistrer des choses plus sérieuses, à différents niveaux (INFO, WARNING et ERROR).

mgiuca
la source
12

Tout d'abord, je seconderai la nomination du framework de journalisation de python . Faites cependant attention à la façon dont vous l'utilisez. Plus précisément: laissez le cadre de journalisation étendre vos variables, ne le faites pas vous-même. Par exemple, au lieu de:

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

assurez-vous de faire:

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

car bien qu'elles se ressemblent, la première version entraîne le coût de repr () même si elle est désactivée . La deuxième version évite cela. De même, si vous roulez le vôtre, je suggérerais quelque chose comme:

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

debug = debug_stdout

appelé via:

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

ce qui, encore une fois, évitera la surcharge si vous le désactivez en faisant:

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

debug = debug_noop

La surcharge de calcul de ces chaînes n'a probablement pas d'importance à moins qu'elles ne soient 1) coûteuses à calculer ou 2) l'instruction de débogage se trouve au milieu, par exemple, d'une boucle n ^ 3 ou quelque chose du genre. Non pas que je sache quoi que ce soit à ce sujet.

pjz
la source
Il y a plus d'informations sur ce sujet important sous `` optimisation '' dans le guide de
Martin CR
7

Je ne sais pas pour les autres, mais j'ai été utilisé pour définir une "constante globale" ( DEBUG) puis une fonction globale ( debug(msg)) qui ne s'imprimerait msgque siDEBUG == True .

Ensuite, j'écris mes instructions de débogage comme:

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

... puis j'ai repris les tests unitaires et je n'ai plus jamais fait ça! :)

Mac
la source
Test unitaire ha. Ok, c'est une autre chose à ramasser alors :(
crazyaboutliv
1
Je ne veux pas décourager les tests unitaires - c'est essentiel. Mais je ne pense pas que ce soit un substitut à la journalisation, même en tant que technique de débogage. Je fais encore beaucoup d'impressions pour tester rapidement les choses.
mgiuca
@crazyaboutliv - Les tests unitaires correctement effectués sont excellents. Jetez un œil à ce chapitre de plongée dans Python pour une présentation rapide, concise et facile à suivre
mac
@mgiuca - Je fais aussi une impression rapide, mais ce n'est que quelques instants pour print()amener mon code au niveau requis pour réussir le test. Je ne me retrouve jamais avec une énorme quantité de print()partout. La journalisation est cool aussi! :)
mac
2
@mac Il semble que votre lien nécessite maintenant un «www» explicite - il est maintenant hébergé ici .
culix