web-dev-qa-db-fra.com

Comment implémentez-vous "#ifdef" en python?

Programmation dans C J'avais l'habitude d'avoir des sections de code uniquement utilisées à des fins de débogage (commandes de journalisation et autres). Ces instructions peuvent être complètement désactivées pour la production en utilisant #ifdef directives de pré-processeur, comme ceci:

 #ifdef MACRO

 controlled text

 #endif /* MACRO */

Quelle est la meilleure façon de faire quelque chose de similaire dans python?

32
dangonfast

Si vous souhaitez simplement désactiver les méthodes de journalisation, utilisez le module logging. Si le niveau de journalisation est défini pour exclure, par exemple, les instructions de débogage, alors logging.debug sera très proche d'un no-op (il vérifie simplement le niveau de journal et retourne sans interpoler la chaîne de journal).

Si vous voulez réellement supprimer des morceaux de code au moment de la compilation du bytecode conditionnel à une variable particulière, votre seule option est le plutôt énigmatique __debug__ variable globale. Cette variable est définie sur True sauf si -O L'indicateur est passé à Python (ou PYTHONOPTIMIZE est défini sur quelque chose de non vide dans l'environnement).

Si __debug__ est utilisé dans une instruction if, l'instruction if est en fait compilée uniquement dans la branche True. Cette optimisation particulière est aussi proche d'une macro de préprocesseur que Python obtient jamais.

Notez que, contrairement aux macros, votre code doit toujours être syntaxiquement correct dans les deux branches de if.


Pour montrer comment __debug__ fonctionne, considérez ces deux fonctions:

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

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

Maintenant, vérifiez-les 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é".

27
nneonneo

Voici un exemple que j'utilise pour faire la distinction entre Python 2 & 3 pour mes Python programmes Tk:

 
 import sys 
 if sys.version_info [0] == 3: 
 from tkinter import * 
 from tkinter import ttk 
 else: 
 de Tkinter import * 
 import ttk 
 
 "" "reste de votre code" "" 

J'espère que c'est une illustration utile.

7
Richard

Il est important de comprendre que dans Python def et class sont deux instructions 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 le préprocesseur en C et C++, vous pouvez utiliser le langage normal Python.

Le langage Python est fondamentalement différent du C et du C++ sur ce point car il n'existe aucun concept de "temps de compilation" et les deux seules phases sont le "temps d'analyse" (lorsque le code source est lu) et le "temps d'exécution" lorsque le code analysé (normalement composé principalement d'instructions de définition mais qui est en effet arbitraire Python) est exécuté.

J'utilise le terme "temps d'analyse" même si techniquement, lorsque le code source est lu dans la transformation, c'est une compilation complète en bytecode parce que la sémantique de la compilation C et C++ est différente et par exemple la définition d'une fonction se produit pendant cette phase ( tandis qu'à la place cela se produit lors de l'exécution en Python).

Même l'équivalent de #include de C et C++ (qui dans 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 (analyse) afin qu'elle puisse être placée à l'intérieur d'un python if. Tout à fait courant est par exemple d'avoir un import à l'intérieur d'un bloc try qui fournira des définitions alternatives pour certaines fonctions si une bibliothèque facultative Python n'est pas présente sur le système.

Enfin, notez que dans Python vous pouvez même créer de nouvelles fonctions et classes à l'exécution à partir de zéro en utilisant exec, sans les avoir nécessairement dans votre code source. Vous pouvez également assembler ces objets utilisant directement du code parce que les classes et les fonctions ne sont en fait que des objets normaux (ceci n'est normalement fait que pour les classes).

Il existe certains outils qui essaient plutôt de considérer les définitions def et class et les instructions import comme "statiques", par exemple pour effectuer une analyse statique de Python code pour générer des avertissements sur les fragments suspects ou pour créer un package déployable autonome qui ne dépend pas d'avoir une installation spécifique Python sur le système pour exécuter le programme. cependant, ils doivent pouvoir considérer que Python est plus dynamique que C ou C++ dans ce domaine et ils permettent également d'ajouter des exceptions pour les cas où l'analyse automatique échouera.

6
6502

Pour autant que je sache, vous devez utiliser de véritables instructions if. Il n'y a pas de préprocesseur, il n'y a donc pas d'analogue aux directives de préprocesseur.

Edit: En fait, il semble que la première réponse à cette question sera plus éclairante: Comment feriez-vous l'équivalent des directives de préprocesseur en Python?

Soi-disant, il existe une variable spéciale __debug__ qui, lorsqu'il est utilisé avec une instruction if, sera évalué une fois, puis non évalué à nouveau lors de l'exécution.

5
Andrew Gorcester

Il n'y a pas d'équivalent direct à ma connaissance, vous pouvez donc vouloir effectuer un zoom arrière et reconsidérer les problèmes que vous avez utilisés pour résoudre à l'aide du préprocesseur.

Si ce n'est que la journalisation de diagnostic que vous recherchez, il existe un module de journalisation complet qui devrait couvrir tout ce que vous vouliez et plus encore.

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

Pour quoi d'autre utilisez-vous le préprocesseur? Configurations de test? Il y a un module de configuration pour ça.

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

Rien d'autre?

2
John Mee

Si vous utilisez #ifdef pour vérifier les variables qui peuvent avoir été définies dans la portée au-dessus du fichier actuel, vous pouvez utiliser des exceptions. Par exemple, j'ai des scripts que je veux exécuter différemment de l'intérieur de ipython par rapport à l'extérieur ipython (afficher les tracés vs enregistrer les tracés, par exemple). J'ajoute donc

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

Cela me laisse une variable ipy, qui me dit si oui ou non __IPYTHON__ a été déclaré dans une étendue au-dessus de mon script actuel. C'est le parallèle le plus proche que je connaisse pour un #ifdef fonction en Python.

Pour ipython, c'est une excellente solution. Vous pouvez utiliser des constructions similaires dans d'autres contextes, dans lesquels un script appelant définit des valeurs variables et les scripts internes vérifient en conséquence. Bien entendu, cela dépendra de votre cas d'utilisation spécifique.

0
mmdanziger

Si vous travaillez sur Spyder, vous n'avez probablement besoin que de ceci:

try:
    print(x)
except:
    #code to run under ifndef
    x = "x is defined now!"
#other code

La première fois que vous exécutez votre script, vous exécuterez le code sous #code to run under ifndef, le second, vous allez le sauter. Esperons que ça marche:)

0
Yanqi Huang

Je ne l'ai pas essayé moi-même mais que diriez-vous https://code.google.com/p/pypreprocessor/

0
Mark Lawrence

Ceci peut être réalisé en passant l'argument de ligne de commande comme ci-dessous:

import sys

my_macro = 0

if(len(sys.argv) > 1):
    for x in sys.argv:
        if(x == "MACRO"):
            my_macro = 1

if (my_macro == 1):
    controlled text

Essayez d'exécuter le script suivant et observez les résultats après cela:

python myscript.py MACRO

J'espère que cela t'aides.

0
DesiKeki