2009-05-14 15 views
8

Quando eseguo il debug, mi piace stampare tutti gli input e gli output di una funzione (so che ho bisogno di un IDE migliore, ma umorami, questo potrebbe essere usato per la segnalazione degli errori). Quindi, preferirei avere:Come scriveresti un decoratore @debuggable in python?

@debuggable 
def myfunc(argA,argB,argC): 
    return argB+1 

e utilizzare una variabile globale per attivare o disattivare il debug. No, non ti piacciono neanche i globali, immaginai.

Il meglio che posso venire in mente è:

DEBUG = True 

def debuggable(func): 
    if DEBUG: 
     def decorated(*args): 
      print "Entering ",func.func_name 
      print " args ",args 
      ret = func(*args) 
      print ret 
      return ret 
     return decorated 
    else: 
     return func 

@debuggable 
def myfunc(this,that): 
    return this+that 

e funzionante:

>>> myfunc(1,3) 
Entering myfunc 
    args (1, 3) 
4 

Come posso migliorare questo?

+0

C'è un post piuttosto lungo sul tema di decoratori di tracing a [Word Aligned] (http://wordaligned.org/articles/echo). –

risposta

22

Utilizzare un debugger. Sul serio. Decorare ogni funzione che vuoi tenere traccia è una cattiva idea.

Python has a debugger included, quindi non è necessario un buon IDE.

Se non si desidera utilizzare un debugger, è possibile utilizzare trace function.

import sys 

@sys.settrace 
def trace_debug(frame, event, arg): 
    if event == 'call': 
     print ("calling %r on line %d, vars: %r" % 
       (frame.f_code.co_name, 
       frame.f_lineno, 
       frame.f_locals)) 
     return trace_debug 
    elif event == "return": 
     print "returning", arg 

def fun1(a, b): 
    return a + b 

print fun1(1, 2) 

che le stampe:

calling 'fun1' on line 14, vars: {'a': 1, 'b': 2} 
returning 3 
3 

ancora più semplice sarebbe quella di utilizzare Winpdb:

Si tratta di un grafica debugger GPL Python indipendente piattaforma con supporto per il debug remoto su una rete, multipla thread, modifica dello spazio dei nomi, debugging incorporato, comunicazione crittografata e fino a 20 volte più veloce di pdb.

Caratteristiche:

  • licenza GPL. Winpdb è software libero.
  • Compatibile con CPython 2.3 o successivo.
  • Compatibile con wxPython 2.6 o successivo.
  • Piattaforma indipendente e testato su Ubuntu Gutsy e Windows XP.
  • Interfacce utente: rpdb2 è basato su console, mentre winpdb richiede wxPython 2.6 o successivo.

Screenshot http://winpdb.org/images/screenshot_winpdb_small.jpg

6

Sono d'accordo con nosklo utilizzando un debugger è molto meglio che scrivere il proprio. Pubblicherò un miglioramento al tuo codice. Ma continuo a pensare che dovresti seguire il consiglio di nosklo.

classi Usa decoratore per rendere il vostro più ordinato debugger:

class Debugger(object): 
    enabled = False 
    def __init__(self, func): 
     self.func = func 

    def __call__(self, *args, **kwargs): 
     if self.enabled: 
      print 'Entering', self.func.func_name 
      print ' args:', args, kwargs 
     return self.func(*args, **kwargs) 

Debugger.enabled = True 

@Debugger 
def myfunc(a, b, c, d): 
    pass 
0

I secondi quello nosklo detto.

Un'altra cosa da notare è che la funzione è un po 'pericoloso:

b = myfunc(1,3) 

In questo caso, "b" è None, perché la funzione decorato non restituisce nulla.

7

Penso che quello che cerchi non sia un decoratore di debug, ma più un decoratore di log.

Potrebbe essere opportuno utilizzare Python's logging module in modo da poter avere un controllo più preciso sulla registrazione stessa. Ad esempio, è possibile eseguire l'output su un file per un'analisi successiva dell'output.

Il decoratore potrebbe quindi cercare qualcosa di più simile:

 

import logging 

logger = logging.getLogger('TraceLog') 
# TODO configure logger to write to file/stdout etc, it's level etc 


def logthis(level): 
    def _decorator(fn): 
     def _decorated(*arg,**kwargs): 
      logger.log(level, "calling '%s'(%r,%r)", fn.func_name, arg, kwargs) 
      ret=fn(*arg,**kwargs) 
      logger.log(level, "called '%s'(%r,%r) got return value: %r", fn.func_name, arg, kwargs, ret) 
      return ret 
     return _decorated 
    return _decorator 

@logthis(logging.INFO) 
def myfunc(this,that): 
    return this+that 
 

Poi, se si configura il logger per l'output stderr che ci si vede:

 

>>> logger.setLevel(logging.INFO) 
>>> handler=logging.StreamHandler() 
>>> logger.addHandler(handler) 
>>> myfunc(1,2) 
calling 'myfunc'((1, 2),{}) 
called 'myfunc'((1, 2),{}) got return value: 3 
 
Problemi correlati