2010-01-05 8 views
9

mi código Python se entrelaza con una gran cantidad de llamadas de función utilizados para (la depuración | perfilado | trazando etc.) por ejemplo:pitón equivalente de 'funC#define()' o cómo hacer comentarios a cabo una llamada de función en 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') 

quiero #definir estas funciones que consumen recursos fuera del código. algo así como el equivalente c

#define logging.debug(val) 

sí, sé el mecanismo de nivel de registro de módulo de registro puede ser utilizado para enmascarar memorizaciones por debajo del nivel de registro conjunto. pero, im pedir de manera general tener las funciones Python intérprete de salto (que toman tiempo para funcionar incluso si no hacen mucho)

una idea es redefinir las funciones que quiero comentar a cabo en función vacía:

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

la idea anterior todavía llama a una función, y puede crear una miríada de otros problemas

Respuesta

17

Python no tiene un preprocesador, aunque puede ejecutar su fuente de python a través de un preprocesador externo para obtener el mismo efecto, p. Ej. sed "/logging.debug/d" quitará todos los comandos de registro de depuración. Sin embargo, esto no es muy elegante: terminará necesitando algún tipo de sistema de compilación para ejecutar todos sus módulos a través del preprocesador y tal vez crear un nuevo árbol de directorios de los archivos .py procesados ​​antes de ejecutar el script principal.

Alternativamente, si coloca todas las instrucciones de depuración en un bloque if __debug__:, se optimizarán cuando python se ejecute con el distintivo -O (optimizar).

Como comentario, revisé el código con el módulo dis para asegurarme de que se optimizó.Descubrí que tanto

if __debug__: doStuff() 

y

if 0: doStuff() 

están optimizados, pero

if False: doStuff() 

no lo es. Esto se debe a que falsa es un objeto de Python regular, y se puede hacer en realidad esto:

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

que me parece un defecto en el idioma - es de esperar que se fija en Python 3.

Edición:

Esto se soluciona en Python 3: Asignar a verdadero o falso now gives a SyntaxError. Desde verdadero y falso son constantes en Python 3, significa que se ha optimizado if False: doStuff():

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

Funciona igual en Python 3. – Brian

+0

antepender si 0: | si __debug__: | if variable: + optimización, ya sea a través de un script o un buen editor puede ser la mejor idea (mejor que anteponer a # que no maneja declaraciones de múltiples líneas).tal vez Python no sea perfecto después de todo –

+1

@Dave: un buen virus python inyectaría la línea 'False, True = True, False' en el código. cosas maravillosas pueden suceder –

0

utilizar un módulo de ámbito variable?

from config_module import debug_flag

y utilizar esta "variable" a la puerta de acceso a la función (s) de registro. Construiría usted mismo un módulo logging que usa el debug_flag para controlar la funcionalidad de registro.

+0

por lo que habría que añadir una comprobación antes de cada llamada a la función? –

+0

@random guy: ¿Más sucio de lo que ya es con todas estas declaraciones de depuración? – cschol

+0

@jldupont: ¿no tendré que cambiar todas las llamadas para iniciar sesión en mi clase contenedora? –

0

Creo que no es posible completar completamente la invocación de una función, ya que Python funciona de una manera diferente que C. La definición # tiene lugar en el precompilador, antes de compilar el código. En Python, no existe tal cosa.

Si desea eliminar por completo la llamada para depurar en un entorno de trabajo, creo que la única forma de cambiar realmente el código antes de la ejecución. Con un script previo a la ejecución, podría comentar/descomentar las líneas de depuración.

Algo como esto:

logging.py Archivo

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

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

call_log.py Archivo

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() 

Por supuesto, tiene sus problemas, especialmente si hay una gran cantidad de módulos y son complejos, pero podrían ser utilizables si necesita evitar por completo la llamada a una función.

-1

No puede saltar llamadas a funciones. Podrías redefinir estos como vacíos, por ejemplo, creando otro objeto de registro que proporciona la misma interfaz, pero con funciones vacías.

Pero, con mucho, el método más limpio es hacer caso omiso de los mensajes de registro de baja prioridad (como usted sugiere):

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

no me he dejado claro. Estoy buscando un python de propósito general para 'llamar' a las llamadas de función. es decir, quiero que ni siquiera se llamen las funciones 'comentadas'. su sugerencia no elimina la llamada de función a logging.debug(). la función se llama, y ​​hace algún procesamiento. esto es lo que quiero evitar –

+0

Usted ha sido claro, pero no creo que sea pitónico hacer lo que usted pidió. – user238424

0

Antes de hacer esto, tiene que perfila para verificar que el registro es en realidad tomando una cantidad sustancial ¿de tiempo? Puede encontrar que pasa más tiempo tratando de eliminar las llamadas que las que ahorra.

A continuación, ¿ha intentado algo como Psyco? Si tiene las cosas configuradas para que el registro esté desactivado, entonces Psyco puede optimizar la mayor parte de la sobrecarga de llamar a la función de registro, notando que siempre regresará sin acción.

Si todavía encuentra que el registro lleva mucho tiempo, puede que desee considerar anular la función de registro dentro de bucles críticos, posiblemente vinculando una variable local a la función de registro o a una función ficticia según corresponda (o seleccionando Ninguno antes de llamarlo).

+0

no me he aclarado. utilicé el módulo de registro y el rendimiento como un ejemplo. hay otras razones para querer comentar funciones, por ejemplo, al eliminar una función del código –

1

Bueno, siempre puede implementar su propio preprocesador simple que hace el truco. O, mejor aún, puede usar uno ya existente. Diga http://code.google.com/p/preprocess/

+0

También hay http://code.google.com/p/pypreprocessor/. –

2

Aunque creo que la pregunta es perfectamente clara y válida (a pesar de las muchas respuestas que sugieren lo contrario), la respuesta corta es "no hay soporte en Python para esto".

La única solución posible que no sea el preprocessor suggestion sería usar algunos bytecode hacking. Ni siquiera voy a empezar a imaginar cómo debería funcionar esto en términos de API de alto nivel, pero a un nivel bajo podría imaginarse examinando objetos de código para secuencias particulares de instrucciones y reescribiéndolos para eliminarlos.

Por ejemplo, mira las dos funciones siguientes:

>>> 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 

Aquí puede escanear para el LOAD_GLOBAL de debug y eliminarlo y todo lo que hasta el objetivo JUMP_IF_FALSE.

Ésta es la función más tradicional de depuración de estilo C() que obtiene muy bien borrado por un preprocesador:

>>> 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 

Aquí buscaría LOAD_GLOBAL de debug y limpie todo hasta el correspondiente CALL_FUNCTION.

Por supuesto, las dos descripciones de lo que harías son mucho más simples de lo que realmente necesitarías para todos los patrones de uso menos simplistas, pero creo que sería factible. Sería un lindo proyecto, si nadie ya lo ha hecho.

0

definir una función que no hace nada, es decir

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

A continuación, sólo sobrecargar todas las funciones que desee deshacerse de con su función, ala

logging.debug = nuzzing 
+0

mi pregunta original incluye una variante de esta solución –

+0

Sí, pero al permitir que su función de marcador de posición se llame recursivamente, deja la puerta abierta para maldad inmediata (se excede la pila de profundidad de llamada máxima). El compilador cpython reciente es lo suficientemente inteligente como para reducir cualquier función que solo pase a un noop de todos modos, por lo que cualquier llamada a ella está efectivamente comentada. No me imagino que vas a encontrar una mejor solución. – richo

0

me gusta el 'si __debug_' solución, excepto que ponerlo en frente de cada llamada es un poco molesto y feo. Tuve el mismo problema y lo superé al escribir una secuencia de comandos que analiza automáticamente los archivos de origen y reemplaza las declaraciones de registro con las declaraciones de paso (y las declaraciones comentadas de las declaraciones de registro). También puede deshacer esta conversión.

Lo uso cuando despliegue código nuevo en un entorno de producción cuando hay muchas declaraciones de registro que no necesito en una configuración de producción y que están afectando el rendimiento.

Puede encontrar el script aquí: http://dound.com/2010/02/python-logging-performance/

Cuestiones relacionadas