Как вы реализуете "#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
Надеюсь это поможет.