Comment implémenter "#ifdef " en python?
programmation dans C
j'avais l'habitude d'avoir des sections de code utilisées uniquement pour le débogage (commandes de journalisation et similaires). Ces instructions pourraient être complètement désactivées pour la production en utilisant les directives #ifdef
pré-processeur, comme ceci:
#ifdef MACRO
controlled text
#endif /* MACRO */
Quelle est la meilleure façon de faire quelque chose de similaire dans python
?
7 réponses
si vous voulez juste désactiver les méthodes de journalisation, utilisez le module logging
. Si le niveau de log est défini pour exclure, disons, les instructions de débogage, alors logging.debug
sera très proche d'un no-op (il vérifie simplement le niveau de log et retourne sans interpoler la chaîne de log).
si vous voulez réellement supprimer des morceaux de code au moment de la compilation de bytecode conditionnée à une variable particulière, votre seule option est la variable globale __debug__
plutôt énigmatique. Ce la variable est définie à True
sauf si le drapeau -O
est passé à Python (ou PYTHONOPTIMIZE
est défini à quelque chose de non vide dans l'environnement).
si __debug__
est utilisé dans une déclaration if
, la déclaration if
est en fait compilée dans la seule branche True
. Cette optimisation particulière est aussi proche d'une macro préprocesseur que Python ne l'obtient jamais.
Notez que, contrairement aux macros, votre code doit toujours être syntaxiquement correct dans les deux branches du if
.
pour montrer comment __debug__
fonctionne, considérer ces deux fonctions:
def f():
if __debug__: return 3
else: return 4
def g():
if True: return 3
else: return 4
vérifiez maintenant avec 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
comme vous pouvez le voir, seul f
est"optimisé".
il est important de comprendre que dans Python def
et class
sont deux déclarations exécutables régulières...
import os
if os.name == "posix":
def foo(x):
return x * x
else:
def foo(x):
return x + 42
...
donc pour faire ce que vous faites avec préprocesseur en C et C++, vous pouvez utiliser le régulier langage Python.
le langage Python est fondamentalement différent de C et C++ sur ce point parce qu'il n'existe pas de concept de "temps de compilation" et que les deux seules phases sont "Temps d'analyse" (lorsque le code source est lu en) et "run time" quand le code parsé (normalement composé principalement d'instructions de définition mais qui est en fait du code Python arbitraire) est exécuté.
j'utilise le terme" parse time " même si techniquement quand le code source est lu dans la transformation est une compilation complète en bytecode parce que la sémantique de C et c++ compilation est différente et par exemple la définition d'une fonction se produit pendant cette phase (alors qu'à la place il se produit à l'exécution en Python).
même l'équivalent de #include
de C et C++ (qui en Python est import
) est une instruction régulière qui est exécutée au moment de l'exécution et non au moment de la compilation (parse) de sorte qu'elle peut être placée dans un python régulier if
. Très commun est par exemple d'avoir un import
dans un bloc try
qui fournira des définitions alternatives pour certaines fonctions si une bibliothèque optionnelle Python spécifique n'est pas présente sur le système.
Enfin, notez qu'en Python vous pouvez même créer de nouvelles fonctions et classes à partir de zéro en utilisant exec
, sans les avoir nécessairement dans votre code source. Vous pouvez aussi assembler ces objets directement en utilisant le code parce que les classes et les fonctions sont en effet juste des objets réguliers (ce qui est normalement fait seulement pour les classes, cependant).
il y a certains outils qui essaient plutôt de considérer def
et class
définitions et import
déclarations comme "statique", par exemple pour faire une analyse statique du code Python pour générer des avertissements sur des fragments suspects ou pour créer un paquet déployable autonome qui ne dépend pas d'avoir une installation Python spécifique sur le système pour exécuter le programme. Tous doivent cependant être en mesure de considérer que Python est plus dynamique que C ou C++ dans ce domaine et ils permettent aussi d'ajouter des exceptions pour l'emplacement de l'analyse automatique échoue.
autant que je sache, vous devez utiliser les déclarations actuelles if
. Il n'y a pas de préprocesseur, donc il n'y a pas d'équivalent aux directives préprocesseur.
Edit: en fait, il semble que la réponse à cette question sera plus éclairante: comment feriez-vous l'équivalent des directives préprocesseur en Python?
il y aurait une variable spéciale __debug__
qui, lorsqu'elle est utilisée avec un if
déclaration, sera évalué une fois et puis ne sera pas évalué à nouveau pendant l'exécution.
voici un exemple que j'utilise pour distinguer entre Python 2 et 3 pour mes programmes 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 """
l'Espérance qui est une illustration utile.
il n'y a pas d'équivalent direct que je connaisse, donc vous pourriez vouloir zoomer et reconsidérer les problèmes que vous avez utilisé pour résoudre en utilisant le préprocesseur.
S'il ne s'agit que de journalisation diagnostique, vous êtes après alors il ya un module de journalisation complète qui devrait couvrir tout ce que vous vouliez et plus.
http://docs.python.org/library/logging.html
quoi d'autre utilisez-vous le préprocesseur pour? Configurations d'essai? Il y a un module de configuration pour ça.
http://docs.python.org/library/configparser.html
autre chose?
si vous utilisez #ifdef
pour vérifier les variables qui peuvent avoir été définies dans la portée au-dessus du fichier courant, vous pouvez utiliser des exceptions. Par exemple, j'ai des scripts que je veux exécuter différemment de dans ipython
vs à l'extérieur ipython
(Afficher des tracés vs enregistrer des tracés, par exemple). Donc j'ajoute
ipy = False
try:
ipy = __IPYTHON__
except NameError:
pass
cela me laisse avec une variable ipy
, qui me dit si oui ou non __IPYTHON__
a été déclaré dans une portée au-dessus de mon script actuel. C'est le parallèle le plus proche que je connaisse pour une fonction #ifdef
en Python.
Pour ipython
, c'est une excellente solution. Vous pouvez utiliser des constructions similaires dans d'autres contextes, dans lesquels un script appelant fixe des valeurs variables et les scripts internes vérifient en conséquence. La question de savoir si cela a un sens ou non dépend évidemment de votre cas d'utilisation spécifique.
Je ne l'ai pas essayé moi-même mais pourquoi pas https://code.google.com/p/pypreprocessor/