python preprocessor

¿Cómo implementas “#ifdef” en python?



preprocessor (7)

Aquí hay un ejemplo que utilizo para distinguir entre Python 2 y 3 para mis programas Python Tk:

import sys if sys.version_info[0] == 3: from tkinter import * from tkinter import ttk else: from Tkinter import * import ttk """ rest of your code """

Espero que sea una ilustración útil.

La programación en C solía tener secciones de código solo para fines de depuración (comandos de registro y similares). Esas declaraciones podrían ser completamente deshabilitadas para la producción usando #ifdef preprocesador #ifdef , como esto:

#ifdef MACRO controlled text #endif /* MACRO */

¿Cuál es la mejor manera de hacer algo similar en python ?


Es importante entender que en Python def y class hay dos sentencias ejecutables regulares ...

import os if os.name == "posix": def foo(x): return x * x else: def foo(x): return x + 42 ...

para hacer lo que haces con el preprocesador en C y C ++ puedes usar el lenguaje Python regular.

El lenguaje Python es fundamentalmente diferente de C y C ++ en este punto porque no existe ningún concepto de "tiempo de compilación" y las dos únicas fases son "tiempo de análisis" (cuando se lee el código fuente) y "tiempo de ejecución" cuando el código analizado (normalmente compuesto principalmente de declaraciones de definición, pero en realidad es un código Python arbitrario) se ejecuta.

Estoy utilizando el término "tiempo de análisis" aunque técnicamente cuando el código fuente se lee en la transformación es una compilación completa a bytecode porque la compilación semántica de C y C ++ es diferente y, por ejemplo, la definición de una función ocurre durante esa fase ( mientras que en cambio ocurre en tiempo de ejecución en Python).

Incluso el equivalente de #include de C y C ++ (que en Python es import ) es una declaración regular que se ejecuta en tiempo de ejecución y no en tiempo de compilación (análisis), por lo que se puede colocar dentro de un python regular if . Bastante común es, por ejemplo, tener una import dentro de un bloque de try que proporcionará definiciones alternativas para algunas funciones si una biblioteca opcional de Python no está presente en el sistema.

Finalmente, tenga en cuenta que en Python puede incluso crear nuevas funciones y clases en tiempo de ejecución desde cero mediante el uso de exec , no necesariamente teniendolas en su código fuente. También puede ensamblar esos objetos directamente usando código porque las clases y funciones son, de hecho, solo objetos normales (esto normalmente se hace solo para clases, sin embargo).

Hay algunas herramientas que, en cambio, tratan de considerar las def y class y las declaraciones de import como "estáticas", por ejemplo, para realizar un análisis estático del código Python para generar advertencias en fragmentos sospechosos o para crear un paquete implementable independiente que no lo haga. depende de tener una instalación específica de Python en el sistema para ejecutar el programa. Sin embargo, todos ellos deben poder considerar que Python es más dinámico que C o C ++ en esta área y también permiten agregar excepciones para los casos en que el análisis automático fallará.



No tengo un equivalente directo, por lo que es posible que desee reducir y reconsiderar los problemas que solía resolver utilizando el preprocesador.

Si lo que busca es solo el registro de diagnóstico, entonces hay un módulo de registro completo que debe cubrir todo lo que desea y más.

http://docs.python.org/library/logging.html

¿Para qué más usas el preprocesador? ¿Configuraciones de prueba? Hay un módulo de configuración para eso.

http://docs.python.org/library/configparser.html

¿Algo más?


Por lo que yo sé, tienes que usar declaraciones reales if . No hay preprocesador, por lo que no hay analógico para las directivas de preprocesador.

Edición: En realidad, parece que la respuesta principal a esta pregunta será más esclarecedora: ¿Cómo haría el equivalente de las directivas de preprocesador en Python?

Supuestamente hay una variable especial __debug__ que, cuando se usa con una sentencia if , se evaluará una vez y luego no se volverá a evaluar durante la ejecución.


Si está utilizando #ifdef para verificar las variables que pueden haber sido definidas en el alcance sobre el archivo actual, puede usar excepciones. Por ejemplo, tengo scripts que quiero ejecutar de forma diferente desde ipython vs outside ipython (muestre los gráficos frente a los gráficos de guardar, por ejemplo). Así que agrego

ipy = False try: ipy = __IPYTHON__ except NameError: pass

Esto me deja con una variable ipy , que me dice si __IPYTHON__ fue declarado o no en un ámbito por encima de mi script actual. Este es el paralelo más cercano que conozco para una función #ifdef en Python.

Para ipython , esta es una gran solución. Podría usar construcciones similares en otros contextos, en los que un script de llamada establece valores variables y los scripts internos se verifican en consecuencia. Si esto tiene sentido o no, por supuesto, dependerá de su caso de uso específico.


Si solo desea deshabilitar los métodos de registro, use el módulo de logging . Si el nivel de registro está configurado para excluir, por ejemplo, las declaraciones de depuración, entonces logging.debug estará muy cerca de no logging.debug (solo verifica el nivel de registro y vuelve sin interpolar la cadena de registro).

Si realmente desea eliminar fragmentos de código en el tiempo de compilación del bytecode condicional en una variable particular, su única opción es la variable global __debug__ enigmática. Esta variable se establece en True menos que el indicador -O se pase a Python (o PYTHONOPTIMIZE se establezca en algo no vacío en el entorno).

Si se usa __debug__ en una sentencia if , la sentencia if se compila realmente solo en la rama True . Esta optimización en particular es lo más parecido a una macro de preprocesador que Python obtiene.

Tenga en cuenta que, a diferencia de las macros, su código todavía debe ser sintácticamente correcto en ambas ramas de if .

Para mostrar cómo funciona __debug__ , considere estas dos funciones:

def f(): if __debug__: return 3 else: return 4 def g(): if True: return 3 else: return 4

Ahora echa un vistazo con dis :

>>> dis.dis(f) 2 0 LOAD_CONST 1 (3) 3 RETURN_VALUE >>> dis.dis(g) 2 0 LOAD_GLOBAL 0 (True) 3 JUMP_IF_FALSE 5 (to 11) 6 POP_TOP 7 LOAD_CONST 1 (3) 10 RETURN_VALUE >> 11 POP_TOP 3 12 LOAD_CONST 2 (4) 15 RETURN_VALUE 16 LOAD_CONST 0 (None) 19 RETURN_VALUE

Como puede ver, solo f está "optimizado".