Как вы реализуете "#ifdef" в python?
Программирование в C
Раньше у меня были разделы кода, используемые только для целей отладки (команды ведения журнала и т.п.). Эти утверждения могут быть полностью отключены для производства с использованием директив #ifdef
для предварительного процессора, например:
#ifdef MACRO
controlled text
#endif /* MACRO */
Каков наилучший способ сделать что-то подобное в python
?
Ответы
Ответ 1
Если вы просто хотите отключить методы ведения журнала, используйте модуль logging
. Если уровень журнала установлен для исключения, скажем, отладочных инструкций, то logging.debug
будет очень близок к no-op (он просто проверяет уровень журнала и возвращает без интерполяции строки журнала).
Если вы хотите на самом деле удалить куски кода в момент компиляции байт-кода в зависимости от конкретной переменной, единственным вариантом является довольно загадочная глобальная переменная __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
.
Ответ 2
Важно понимать, что в Python def
и class
есть два регулярных исполняемых оператора...
import os
if os.name == "posix":
def foo(x):
return x * x
else:
def foo(x):
return x + 42
...
чтобы делать то, что вы делаете с препроцессором в C и С++, вы можете использовать обычный язык Python.
Язык Python принципиально отличается от C и С++ в этой точке, потому что не существует понятия "время компиляции", и только две фазы: "время синтаксического анализа" (когда считывается исходный код) и "время выполнения", когда выполняется обработанный код (обычно в основном состоящий из операторов определения, но это действительно произвольный код Python).
Я использую термин "время синтаксического анализа", даже если технически, когда исходный код считывается в преобразовании, это полная компиляция для байт-кода, потому что семантика компиляции C и С++ различна, и, например, определение функции происходит во время (а вместо этого это происходит во время выполнения на Python).
Даже эквивалент #include
для C и С++ (который в Python равен import
) - это регулярный оператор, который выполняется во время выполнения, а не в момент компиляции (разбора), поэтому его можно разместить внутри обычного питона if
. Весьма распространено, например, иметь import
внутри блока try
, который будет предоставлять альтернативные определения для некоторых функций, если в системе нет конкретной необязательной библиотеки Python.
Наконец, обратите внимание, что в Python вы даже можете создавать новые функции и классы во время выполнения с нуля с помощью exec
, не обязательно имея их в исходном коде. Вы также можете собирать эти объекты напрямую с помощью кода, потому что классы и функции - это действительно обычные объекты (это обычно делается только для классов).
Есть несколько инструментов, которые вместо этого пытаются рассматривать определения def
и class
и import
как "статические", например, для статического анализа кода Python для генерации предупреждений о подозрительных фрагментах или для создания автономный развертываемый пакет, который не зависит от наличия в системе конкретной установки Python для запуска программы. Тем не менее, все они должны уметь считать, что Python более динамичен, чем C или С++ в этой области, а также позволяет добавлять исключения для того, где автоматический анализ завершится с ошибкой.
Ответ 3
Вот пример, который я использую, чтобы различать Python 2 и 3 для моих программ 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 """
Надеюсь, что это полезная иллюстрация.
Ответ 4
Насколько мне известно, вы должны использовать фактические операторы if
. Препроцессора нет, поэтому нет аналогов директивам препроцессора.
Изменить: На самом деле, похоже, что лучший ответ на этот вопрос будет более освещающим: Как бы вы выполняли эквивалент препроцессорных директив в Python?
Предположительно существует специальная переменная __debug__
, которая при использовании с оператором if
будет оцениваться один раз, а затем не будет оцениваться снова во время выполнения.
Ответ 5
Нет прямого эквивалента, о котором я знаю, поэтому вы можете уменьшить масштаб и пересмотреть проблемы, которые вы использовали для решения, используя препроцессор.
Если это просто диагностическое ведение журнала, то после этого есть полный модуль регистрации, который должен охватывать все, что вам нужно, и многое другое.
http://docs.python.org/library/logging.html
Для чего еще используется препроцессор? Конфигурации тестирования? Для этого есть конфигурационный модуль.
http://docs.python.org/library/configparser.html
Что-нибудь еще?
Ответ 6
Если вы используете #ifdef
для проверки переменных, которые могут быть определены в области выше текущего файла, вы можете использовать исключения. Например, у меня есть сценарии, которые я хочу использовать по-разному в пределах ipython
и вне ipython
(например, показывать графики и сохранять графики). Поэтому я добавляю
ipy = False
try:
ipy = __IPYTHON__
except NameError:
pass
Это оставляет меня переменной ipy
, которая сообщает мне, был ли объявлен __IPYTHON__
в области выше моего текущего script. Это ближайшая параллель, которую я знаю для функции #ifdef
в Python.
Для ipython
это отличное решение. Вы можете использовать аналогичные конструкции в других контекстах, в которых вызывающий script устанавливает значения переменных и внутренние скрипты соответственно. Независимо от того, имеет ли это смысл, конечно, будет зависеть от вашего конкретного варианта использования.
Ответ 7
Я сам не пробовал, но как насчет https://code.google.com/p/pypreprocessor/
Ответ 8
Если вы работаете над Spyder, вам, вероятно, нужно только это:
try:
print(x)
except:
#code to run under ifndef
x = "x is defined now!"
#other code
В первый раз, когда вы запустите свой скрипт, вы запустите код в #code to run under ifndef
, во-вторых, вы пропустите его.
Надеюсь, что это работает:)
Ответ 9
Это может быть достигнуто путем передачи аргумента командной строки, как показано ниже:
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
Надеюсь, это поможет.