2010-01-05 11 views
9

mio codice Python si intreccia con un sacco di chiamate di funzione utilizzati per (debugging | profiling | tracing etc.) ad esempio:pitone equivalente di '#define func()' o come commentare una chiamata di funzione in Python

import logging 

logging.root.setLevel(logging.DEBUG) 
logging.debug('hello') 
j = 0 
for i in range(10): 
    j += i 
    logging.debug('i %d j %d' % (i,j)) 
print(j) 
logging.debug('bye') 

voglio # definire queste funzioni che consumano risorse fuori dal codice. qualcosa come l'equivalente c

#define logging.debug(val) 

sì, lo so il meccanismo di livello di registrazione modulo di registrazione può essere utilizzato per mascherare memorizzazioni di sotto del livello di log set. ma, im chiedendo un modo generale di avere le funzioni di interprete Python di salto (che richiedono tempo per l'esecuzione, anche se essi non fare molto)

uno idea è quella di ridefinire le funzioni voglio commentare in funzioni vuote:

def lazy(*args): pass 
logging.debug = lazy 

l'idea sopra chiama ancora una funzione, e possono creare una miriade di altri problemi

risposta

17

Python non ha un preprocessore, sebbene sia possibile eseguire la sorgente Python tramite un preprocessore esterno per ottenere lo stesso effetto - ad es. sed "/logging.debug/d" eliminerà tutti i comandi di registrazione debug. Tuttavia, non è molto elegante: finirai per avere bisogno di una sorta di sistema di compilazione per eseguire tutti i moduli attraverso il preprocessore e magari creare un nuovo albero di directory dei file .py elaborati prima di eseguire lo script principale.

In alternativa, se si mettono tutte le istruzioni di debug in un blocco if __debug__:, verranno ottimizzate quando python viene eseguito con il flag -O (ottimizzazione).

Per inciso, ho controllato il codice con il modulo dis per assicurarmi che venisse ottimizzato.Ho scoperto che entrambi

if __debug__: doStuff() 

e

if 0: doStuff() 

sono ottimizzati, ma

if False: doStuff() 

non è. Questo perché False è un oggetto Python regolare, e in effetti si può fare questo:

>>> False = True 
>>> if False: print "Illogical, captain" 
Illogical, captain 

che mi sembra un difetto nel linguaggio - spero che è fissato in Python 3.

Edit:

Questo è corretto in Python 3: Assegnazione a Vero o Falso now gives a SyntaxError. Dal Vero e Falso sono costanti in Python 3, significa che if False: doStuff() è ora ottimizzato:

>>> def f(): 
...  if False: print("illogical") 
... 
>>> dis.dis(f) 
    2   0 LOAD_CONST    0 (None) 
       3 RETURN_VALUE   
+0

Funziona allo stesso modo in Python 3. – Brian

+0

prepending se 0: | se __debug__: | se variabile: + ottimizzazione, sia attraverso uno script o un buon editor può essere l'idea migliore (meglio di prepending # che non gestisce le istruzioni multi linea).forse python non è perfetto dopotutto –

+1

@Dave: un bel virus python inietterebbe nel codice la riga 'False, True = True, False'. cose meravigliose possono accadere –

0

utilizzare un modulo ambito variabile?

from config_module import debug_flag

Utilizzando questo "variabile" per l'accesso porta alla funzione di registrazione (s). Costruirai un modulo logging che utilizza lo debug_flag per accedere alla funzionalità di registrazione.

+0

così avrei dovuto aggiungere un controllo prima di ogni chiamata di funzione? –

+0

@random guy: Dirtier di quanto non sia già con tutte queste affermazioni di debug ?? – cschol

+0

@jldupont: non dovrei cambiare tutte le chiamate per accedere alla mia classe wrapper? –

0

Penso che non sia possibile eseguire il richiamo completo di una funzione su una funzione, poiché Python funziona diversamente C. Il #define si svolge nel pre-compilatore, prima che il codice venga compilato. In Python, non esiste una cosa del genere.

Se si desidera rimuovere completamente la chiamata per eseguire il debug in un ambiente di lavoro, penso che l'unico modo per modificare effettivamente il codice prima dell'esecuzione. Con uno script precedente all'esecuzione è possibile commentare/rimuovere le righe di debug.

Qualcosa di simile a questo:

File logging.py

#Main module 
def log(): 
    print 'logging' 

def main(): 
    log() 
    print 'Hello' 
    log() 

File call_log.py

import re 
#To log or not to log, that's the question 
log = True 

#Change the loging 
with open('logging.py') as f: 
    new_data = [] 
    for line in f: 
     if not log and re.match(r'\s*log.*', line): 
     #Comment 
     line = '#' + line 
     if log and re.match(r'#\s*log.*', line): 
     #Uncomment 
     line = line[1:] 
     new_data.append(line) 

#Save file with adequate log level 
with open('logging.py', 'w') as f: 
    f.write(''.join(new_data)) 


#Call the module 
import logging 
logging.main() 

Naturalmente, ha i suoi problemi, specialmente se ci sono un sacco di moduli e sono complessi, ma potrebbero essere utilizzabili se è necessario evitare assolutamente la chiamata a una funzione.

-1

Non può saltare chiamate di funzione. Potresti ridefinire questi come vuoti, ad es. creando un altro oggetto di log che fornisce la stessa interfaccia, ma con funzioni vuote.

Ma di gran lunga l'approccio più pulito è quello di ignorare i messaggi di log a bassa priorità (come lei ha suggerito):

logging.root.setLevel(logging.CRITICAL) 
+1

non mi sono fatto chiaro. Sto cercando un hack per scopi generici per "commentare" le chiamate di funzione. cioè, voglio che le funzioni "commentate" non vengano nemmeno chiamate. il tuo suggerimento non elimina la funzione chiamata a logging.debug(). la funzione viene chiamata e esegue qualche elaborazione. questo è quello che voglio evitare –

+0

Ti sei chiarito, ma non penso che sia sontuoso fare ciò che hai chiesto. – user238424

0

Prima di fare questo, hai profilati per verificare che la registrazione è in realtà prendendo una notevole quantità di tempo? Potresti scoprire che passi più tempo a cercare di rimuovere le chiamate di quante ne hai risparmiate.

Quindi, hai provato qualcosa come Psyco? Se hai impostato le cose in modo tale che la registrazione sia disabilitata, Psyco potrebbe essere in grado di ottimizzare la maggior parte del sovraccarico di chiamare la funzione di registrazione, notando che ritornerà sempre senza azione.

Se si riscontra ancora una notevole quantità di tempo di registrazione, è possibile che si desideri sovrascrivere la funzione di registrazione all'interno di loop critici, eventualmente associando una variabile locale alla funzione di registrazione o a una funzione fittizia, a seconda dei casi (o controllando None prima di chiamarlo).

+0

non mi sono chiarito. ho usato il modulo di registrazione e le prestazioni come esempio. ci sono altri motivi per voler commentare le funzioni, ad esempio, quando si elimina una funzionalità dal codice –

1

Bene, è sempre possibile implementare il proprio semplice preprocessore che fa il trucco. O, ancora meglio, puoi usare uno già esistente. Dire http://code.google.com/p/preprocess/

+0

C'è anche http://code.google.com/p/pypreprocessor/. –

2

Anche se penso che la domanda sia perfettamente chiara e valida (nonostante le molte risposte che suggeriscono diversamente), la risposta breve è "non c'è supporto in Python per questo".

L'unica soluzione potenziale diversa dallo preprocessor suggestion sarebbe utilizzare bytecode hacking. Non inizierò nemmeno a immaginare come dovrebbe funzionare in termini di API di alto livello, ma a un livello basso si potrebbe immaginare di esaminare gli oggetti del codice per particolari sequenze di istruzioni e riscriverle per eliminarle.

Per esempio, guardate le seguenti due funzioni:

>>> def func(): 
... if debug: # analogous to if __debug__: 
...  foo 
>>> dis.dis(func) 
    2   0 LOAD_GLOBAL    0 (debug) 
       3 JUMP_IF_FALSE   8 (to 14) 
       6 POP_TOP 

    3   7 LOAD_GLOBAL    1 (foo) 
      10 POP_TOP 
      11 JUMP_FORWARD    1 (to 15) 
     >> 14 POP_TOP 
     >> 15 LOAD_CONST    0 (None) 
      18 RETURN_VALUE 

Qui si può eseguire la scansione delle LOAD_GLOBAL di debug, ed eliminarlo e tutto ciò fino al JUMP_IF_FALSE bersaglio.

Questa è la funzione più tradizionale in stile C di debug() che viene piacevolmente cancellato da un preprocessore:

>>> def func2(): 
... debug('bar', baz) 
>>> dis.dis(func2) 
    2   0 LOAD_GLOBAL    0 (debug) 
       3 LOAD_CONST    1 ('bar') 
       6 LOAD_GLOBAL    1 (baz) 
       9 CALL_FUNCTION   2 
      12 POP_TOP 
      13 LOAD_CONST    0 (None) 
      16 RETURN_VALUE 

Qui si dovrebbe cercare di LOAD_GLOBALdebug e pulire tutto fino alla corrispondente CALL_FUNCTION.

Ovviamente, entrambe le descrizioni di ciò che faresti sono molto più semplici di quelle che avresti davvero bisogno per tutti tranne i più semplicistici schemi di utilizzo, ma penso che sarebbe fattibile. Farebbe un progetto carino, se nessuno lo ha già fatto.

0

definire una funzione che non fa nulla, cioè

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

Poi basta sovraccaricare tutte le funzioni che si desidera sbarazzarsi di con la vostra funzione, ala

logging.debug = nuzzing 
+0

la mia domanda originale include una variante di questa soluzione –

+0

Sì, ma lasciando che la funzione del segnaposto si chiami in modo ricorsivo, si sta lasciando la porta aperta per cattiva reputazione (max stack di chiamate in eccesso superato). Il recente compilatore cpython è abbastanza intelligente da ridurre qualsiasi funzione che sia passata a un noop in ogni caso, quindi qualsiasi chiamata ad esso viene effettivamente commentata. Non immagino che troverai una soluzione migliore. – richo

0

Mi piace la 'se __debug_' soluzione salvo che metterlo di fronte a ogni chiamata è un po 'fastidioso e distratto. Ho avuto questo stesso problema e l'ho superato scrivendo uno script che analizza automaticamente i tuoi file sorgente e sostituisce le dichiarazioni di registrazione con le dichiarazioni dei passaggi (e commenta le copie delle istruzioni di registrazione). Può anche annullare questa conversione.

Lo uso quando distribuisco un nuovo codice in un ambiente di produzione quando ci sono molte istruzioni di registrazione che non sono necessarie in un'impostazione di produzione e che influiscono sulle prestazioni.

Potete trovare lo script qui: http://dound.com/2010/02/python-logging-performance/

Problemi correlati