Как вы реализуете "#ifdef" в Python?

Программирование в C Раньше я имел разделы кода, используемые только для целей отладки (команды регистрации и тому подобное). Эти заявления могут быть полностью отключены для производства с помощью #ifdef директивы препроцессора, например:

 #ifdef MACRO

 controlled text

 #endif /* MACRO */

Каков наилучший способ сделать что-то подобное в python?

7 ответов

Решение

Если вы просто хотите отключить методы ведения журнала, используйте logging модуль. Если уровень журнала настроен на исключение, скажем, отладочных операторов, то logging.debug будет очень близко к запрету операций (он просто проверяет уровень журнала и возвращает без интерполяции строки журнала).

Если вы хотите на самом деле удалить куски кода во время компиляции байт-кода при условии определенной переменной, ваш единственный вариант довольно загадочный __debug__ глобальная переменная. Эта переменная установлена ​​в True если только -O флаг передается в Python (или PYTHONOPTIMIZE настроен на что-то непустое в окружающей среде).

Если __debug__ используется в if заявление, if заявление на самом деле составлено только в True ветка. Эта конкретная оптимизация так же близка к макросу препроцессора, как когда-либо получал Python.

Обратите внимание, что, в отличие от макросов, ваш код должен быть синтаксически правильным в обеих ветвях if,


Чтобы показать как __debug__ работает, рассмотрим эти две функции:

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

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

Теперь проверьте их с 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        

Как видите, только f "оптимизирован".

Важно понимать, что в Python def а также class два обычных исполняемых оператора...

import os

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

поэтому, чтобы делать то, что вы делаете с препроцессором в C и C++, вы можете использовать обычный язык Python.

Язык Python в этом отношении принципиально отличается от C и C++, потому что не существует понятия "время компиляции", и единственными двумя фазами являются "время анализа" (когда читается исходный код) и "время выполнения", когда анализируемый код (обычно состоит из определений, но это действительно произвольный код Python).

Я использую термин "время разбора", даже если технически, когда исходный код читается в преобразовании, это полная компиляция в байт-код, потому что семантика компиляции C и C++ отличается, и, например, определение функции происходит во время этой фазы (в то время как вместо этого это происходит во время выполнения в Python).

Даже эквивалент #include C и C++ (что в Python import) является регулярным оператором, который выполняется во время выполнения, а не во время компиляции (анализа), поэтому его можно поместить в обычный питон if, Довольно распространенным является, например, наличие import внутри try блок, который будет предоставлять альтернативные определения для некоторых функций, если в системе нет определенной необязательной библиотеки Python.

Наконец, обратите внимание, что в Python вы можете даже создавать новые функции и классы во время выполнения с нуля, используя exec, не обязательно иметь их в вашем исходном коде. Вы также можете собрать эти объекты напрямую, используя код, потому что классы и функции действительно являются обычными объектами (однако, обычно это делается только для классов).

Есть некоторые инструменты, которые вместо этого пытаются рассмотреть def а также class определения и import операторы как "статические", например, для статического анализа кода Python для генерации предупреждений о подозрительных фрагментах или для создания автономного развертываемого пакета, который не зависит от наличия конкретной установки Python в системе для запуска программы. Однако все они должны иметь возможность учитывать, что Python более динамичен, чем C или C++ в этой области, и они также позволяют добавлять исключения для случаев, когда автоматический анализ завершится неудачей.

Вот пример, который я использую, чтобы различать Python 2 и 3 для моих программ Python Tk:

импорт системы
if sys.version_info[0] == 3:
    из ткинтера импорт *
    от ткинтер импорт ттк
еще:
    от Ткинтер импорт *
    импорт ттк

"" "остальная часть вашего кода" ""

Надеюсь, что это полезная иллюстрация.

Насколько я знаю, вы должны использовать фактические if заявления. Препроцессора нет, поэтому нет аналога директивам препроцессора.

Изменить: На самом деле, похоже, что главный ответ на этот вопрос будет более осветительным: как бы вы сделали эквивалент директив препроцессора в Python?

Предположительно, есть специальная переменная __debug__ который при использовании с if оператор, будет оцениваться один раз, а затем не будет оцениваться снова во время выполнения.

Я не знаю прямого эквивалента, поэтому вы можете уменьшить масштаб и пересмотреть проблемы, которые вы использовали для решения с помощью препроцессора.

Если вам нужны только диагностические журналы, то есть комплексный модуль журналирования, который должен охватывать все, что вы хотели, и даже больше.

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

Для чего еще вы используете препроцессор? Тестовые конфигурации? Для этого есть модуль конфигурации.

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

Что-нибудь еще?

Если вы используете #ifdef чтобы проверить переменные, которые могли быть определены в области над текущим файлом, вы можете использовать исключения. Например, у меня есть сценарии, которые я хочу запустить по-другому изнутри ipython против снаружи ipython (показать графики против сохранения графиков, например). Итак, я добавляю

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

Это оставляет меня с переменной ipy, который говорит мне, или нет __IPYTHON__ был объявлен в области выше моего текущего сценария. Это самая близкая параллель, которую я знаю для #ifdef функция в Python.

За ipythonэто отличное решение. Вы можете использовать аналогичные конструкции в других контекстах, в которых вызывающий скрипт устанавливает значения переменных, а внутренние скрипты проверяют соответственно. Будет ли это иметь смысл, конечно, будет зависеть от вашего конкретного случая использования.

Я не пробовал сам, но как насчет https://code.google.com/p/pypreprocessor/

Если вы работаете над Spyder, вам, вероятно, нужно только это:

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

В первый раз, когда вы запустите свой скрипт, вы запустите код в #code to run under ifndefВо-вторых, вы пропустите это. Надеюсь, что это работает:)

Это может быть достигнуто путем передачи аргумента командной строки, как показано ниже:

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

Попробуйте запустить следующий скрипт и просмотрите результаты после этого:

python myscript.py MACRO

Надеюсь это поможет.

Другие вопросы по тегам