Как бы вы сделали эквивалент директив препроцессора в Python?

Есть ли способ выполнить следующие директивы препроцессора в Python?

#if DEBUG

< do some code >

#else

< do some other code >

#endif

person intrepion    schedule 27.01.2009    source источник


Ответы (7)


Есть __debug__, это специальное значение, которое компилятор предварительно обрабатывает.

if __debug__:
  print "If this prints, you're not running python -O."
else:
  print "If this prints, you are running python -O!"

__debug__ будет заменено компилятором на константу 0 или 1, а оптимизатор удалит все if 0: строки до того, как ваш исходный код будет интерпретирован.

person habnabit    schedule 27.01.2009
comment
Вау, это определенно отвечает на мой вопрос! - person intrepion; 27.01.2009
comment
Проблема с решением заключается в том, что по умолчанию debug имеет значение true, а false только в том случае, если вы запускаете python с параметром командной строки -O. Я обнаружил, что этот переключатель обычно не используется, что не всегда соответствует ожиданиям пользователя. - person Moe; 27.01.2009
comment
@Moe: Кажется, что логика флага обратная. если debug оценивается как True, я ожидаю, что я работаю в режиме отладки, что не так. - person Bill the Lizard; 28.01.2009
comment
Но это позволяет вам иметь утверждения в разработке, которые удаляются на рабочем сервере, если вы используете там только -O. :-) Я не думал, что это возможно, пока не прочитал это! - person jonny; 26.11.2015
comment
Это также заставило меня узнать, что assert использует __debug__, что делает утверждения разработки еще более осязаемыми: docs.python.org/2/reference/simple_stmts.html#assert - person jonny; 27.11.2015
comment
я согласен с @Moe. это отстало и сбивает с толку. даже зная, что есть такая функция, мне все равно пришлось гуглить, что это такое, и найти этот ответ. происходит каждый раз. обычно никто не запускает python с параметром -O, ну да ладно... - person victor n.; 23.01.2016
comment
Как бы вы применили его к декораторам простым способом? - person Ricardo Stuven; 05.04.2019
comment
-O означает оптимизацию, как и в других языках, здесь нет ничего обратного. - person lorenzo; 12.06.2020

Я написал препроцессор Python под названием pypreprocessor, который делает именно то, что вы описываете.

Исходный код и документация доступны на GitHub.

Пакет также можно загрузить/установить через PyPI.

Вот пример выполнения того, что вы описываете.

from pypreprocessor import pypreprocessor

pypreprocessor.parse()

#define debug

#ifdef debug
print('The source is in debug mode')
#else
print('The source is not in debug mode')
#endif

pypreprocessor способен на гораздо большее, чем просто предварительная обработка «на лету». Чтобы увидеть больше примеров использования, ознакомьтесь с проектом в Google Code.

Обновление: дополнительная информация о pypreprocessor

Способ, которым я выполняю предварительную обработку, прост. В приведенном выше примере препроцессор импортирует объект pypreprocessor, созданный в модуле pypreprocessor. Когда вы вызываете parse() в препроцессоре, он сам потребляет файл, в который он импортирован, и генерирует свою временную копию, которая комментирует весь код препроцессора (чтобы препроцессор не вызывал себя рекурсивно в бесконечном цикле) и закомментируйте все неиспользуемые части.

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

Затем сгенерированный файл, содержащий код постобработки, выполняется «на лету».

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

Недостатком (и моей первоначальной причиной создания модуля) является то, что вы не можете запускать как python 2x, так и python 3x в одном и том же файле, потому что интерпретатор pythons выполняет полную проверку синтаксиса перед выполнением кода и отклонит любой код конкретной версии перед выполнением. препроцессору разрешен запуск ::sigh::. Моя первоначальная цель состояла в том, чтобы иметь возможность разрабатывать 2x и 3x код бок о бок в одном файле, который создавал бы байт-код для конкретной версии в зависимости от того, на чем он работает.

В любом случае, модуль препроцессора по-прежнему очень полезен для реализации общих возможностей предварительной обработки в стиле C. Кроме того, препроцессор может выводить постобработанный код в файл для последующего использования, если вы хотите.

Кроме того, если вы хотите сгенерировать версию, в которой удалены все директивы препроцессора, а также любые исключенные #ifdef, это так же просто, как установить флаг в коде препроцессора перед вызовом parse(). Это делает удаление нежелательного кода из исходного файла конкретной версии одношаговым процессом (по сравнению с обходом кода и удалением операторов if вручную).

person Evan Plaice    schedule 07.06.2010
comment
К сожалению, эти пакеты, похоже, больше не поддерживаются, и в настоящее время даже установка через pip install не работает (кто-то уже задокументировал эту проблему в репозитории). - person David S.; 29.03.2020

Я подозреваю, что ты возненавидишь этот ответ. То, как вы это делаете в Python,

# code here
if DEBUG:
   #debugging code goes here
else:
   # other code here.

Поскольку python является интерпретатором, нет необходимости применять этап предварительной обработки и нет особого преимущества в наличии специального синтаксиса.

person Charlie Martin    schedule 27.01.2009
comment
Быть переводчиком не имеет к этому никакого отношения. Никто не утверждает, что Java интерпретируется, хотя в Java используется точно такая же техника (еще один пример — язык D). Python на самом деле является компилятором, он компилирует исходный код в байт-код и выполняет его на виртуальной машине, как и Java. - person Greg Hewgill; 27.01.2009
comment
@Greg Hewgill: в директивах препроцессора нет никакой ценности для улучшения таких вещей, как объявления статического типа или условный код, поскольку один не существует, а другой не представляет значительных затрат. - person S.Lott; 27.01.2009
comment
Грег, вернись и подумай над этим ответом. (1) Java, в отличие от Python, имеет отдельную фазу компиляции. (2) Хотя они так и не стали популярными, было несколько препроцессоров Java. ... - person Charlie Martin; 28.01.2009
comment
(продолжение) Теперь вопрос викторины: что делает препроцессор более выгодным в C/C++, чем в Python? - person Charlie Martin; 28.01.2009
comment
@Charlie После запуска препроцессора (и он хорошо написан) он сможет удалить все неиспользуемые мета-операторы. Поэтому IL (промежуточный язык) или ByteCode, если вы используете python, будут содержать только тот код, который используется для этого конкретного условия препроцессора. Если вы просто разбросаете операторы if/else, все эти операторы все равно нужно будет проверять на работоспособность каждый раз при запуске кода (даже после применения оптимизаций). - person Evan Plaice; 06.02.2011
comment
@Charlie (продолжение) Есть очевидное преимущество в том, что скомпилированный код не нужно лексировать/анализировать после компиляции, поэтому препроцессор C++ будет работать быстрее, чем препроцессор Python, но только при первом выполнении. После того, как ByteCode создан, этап лексера/парсера полностью пропускается, если только ваши директивы препроцессора не меняются постоянно. Другим серьезным недостатком препроцессора Python является то, что вы не можете контролировать то, что делает стадия лексера/парсера в самом интерпретаторе. - person Evan Plaice; 06.02.2011
comment
@Charlie (продолжение) IE, если код, найденный в метаданных препроцессора, недействителен для Python, он выдаст ошибку в интерпретаторе. Вместо проверки семантики «на лету» он делает все за один раз. Если бы вы могли задержать этот шаг, вы могли бы фактически иметь один исходный файл, содержащий код, который можно запускать на нескольких разных языках (при условии, что этот язык содержит препроцессор, который может выполнять ту же функциональность); или код, способный работать в нескольких версиях языка (IE, py2x и py3k в одном файле). - person Evan Plaice; 06.02.2011
comment
@Charlie (продолжение) Первоначально я написал pypreprocessor для использования в качестве инструмента для обновления библиотек python (pypi), чтобы они могли работать как в py3k, так и в python2x. К сожалению, я на своей шкуре обнаружил, что невозможно создать настоящий препроцессор только на python, потому что настоящий препроцессор фактически выполняется в интерпретаторе до лексирования/анализа исходного кода. Лучшим вариантом было бы включить настоящий препроцессор в Python, но из-за безудержного злоупотребления заменами макросов в C разработчики ядра не будут касаться темы препроцессора 10-футовым шестом. - person Evan Plaice; 06.02.2011
comment
@Charlie (продолжение) Отстойная часть любого препроцессора, который изначально не поддерживается в интерпретаторе, заключается в том, что вам все равно нужно создать временный файл, содержащий код после преобразования и предварительной компиляции/предварительного выполнения, потому что он все еще должен быть скомпилирован/выполнен после того, как препроцессор закончит свою работу. Если он включен в интерпретатор, было бы просто добавить дополнительный шаг лексера/парсера препроцессора до фактического исходного лексера/парсера и TextStream вывода в память для передачи по цепочке. - person Evan Plaice; 06.02.2011
comment
@Charlie (продолжение) Основным недостатком использования Java в качестве препроцессора является то, что для такой простой задачи Java требует массивной основы. Основным недостатком использования C/C++ является то, что исполняемый файл препроцессора будет зависеть от платформы. Не проблема, если вы работаете только на одной платформе, но это как бы лишает вас одного из основных преимуществ использования python. В идеале в интерпретаторе должен быть препроцессор, потому что интерпретатор и так уже абстрагируется от специфичных для платформы деталей. - person Evan Plaice; 06.02.2011
comment
@Charlie (продолжение) Основным недостатком включения его в интерпретатор является то, что 99,9% сценариев Python никогда не будут использовать препроцессор, поэтому дополнительный лексер / процессор, необходимый для работы препроцессора, просто добавляет ненужные накладные расходы. Было бы просто добавить параметр для включения препроцессора в самом скрипте, но python (который подчеркивает простоту) не совсем известен тем, что добавляет мета-параметры в свой интерпретатор (потому что это, по сути, уродливый хак). - person Evan Plaice; 06.02.2011
comment
@Charlie (продолжение) Извините за длинные ответы. Я, очевидно, потратил много времени на размышления/работу над этой проблемой, потому что я вижу потребность в инструменте, который делает возможным параллельное кодирование в py2x/py3k. При этом миграция исходной библиотеки для поддержки кода py3k может занять 2-3 года по сравнению с ожидаемым в настоящее время переходом на 5-10 лет. Я говорю об источнике PYPI (индекс пакетов Python), а не об одноразовых сценариях среднего Джо. ИМХО, поддержки Py3k не хватает, и она будет продолжать отстой, пока не будет простого способа поддержать переход. - person Evan Plaice; 06.02.2011
comment
ДАВНО я не смотрел на это, @EvanPlaice, извини. Вот моя точка зрения: существует множество препроцессоров. У питона его нет. Если вы хотите делать то, что может делать препроцессор, используйте один из них. Но если вы хотите сделать это в Python, вы делаете это с помощью кода Python. - person Charlie Martin; 17.06.2016

Вы можете использовать препроцессор в Python. Просто запустите свои скрипты через cpp (C-Preprocessor) в вашем каталоге bin. Однако я сделал это с Lua, и преимущества простой интерпретации перевешивают более сложную компиляцию ИМХО.

person Robert Gould    schedule 27.01.2009
comment
Я согласен, это звучит гораздо больше проблем, чем оно того стоит! - person intrepion; 27.01.2009
comment
FWIW Я уже давно использовал именно этот метод, работая с другим интерпретируемым языком, PostScript, и нашел его очень полезным - в основном для #include'ing файлов и #ifdef'ing операторов, не столько для подстановки макросов. С этим не было особых дополнительных проблем — всего несколько дополнительных вещей в make-файлах, которые существовали, потому что также использовался C++. Пипрепроцессор @Evan Plaice звучит как нечто, на что стоит обратить внимание. - person martineau; 15.11.2010
comment
Кстати: мне пришлось использовать специальный аргумент командной строки с препроцессором C, чтобы сохранить комментарии, потому что использование PostScript // конфликтует с C/C++. Это, вероятно, также необходимо сделать, чтобы использовать его с Python, который имеет // оператор целочисленного деления. - person martineau; 15.11.2010

Вы можете просто использовать обычные языковые конструкции:

DEBUG = True
if DEBUG:
  # Define a function, a class or do some crazy stuff
  def f():
    return 23
else:
  def f():
    return 42
person phihag    schedule 27.01.2009
comment
Было бы идеально, если бы можно было использовать его в модулях. Однако кажется, что DEBUG будет виден только для одного файла, и вы будете использовать дополнительные механизмы для его распространения по файлам... - person ntg; 23.08.2017

Альтернативный метод — использовать сценарий bash для комментирования частей кода, которые имеют отношение только к отладке. Ниже приведен пример скрипта, который комментирует строки, в которых есть оператор '#DEBUG'. Он также может снова удалить эти маркеры комментариев.

if [ "$1" == "off" ]; then
  sed -e '/^#/! {/#DEBUG/ s/^/#/}' -i *.py
  echo "Debug mode to $1"
elif [ "$1" == "on" ]; then
  sed -e '/#DEBUG/ s/^#//' -i *.py
  echo "Debug mode to $1"
else
  echo "usage: $0 on | off"
fi
person slierp    schedule 07.07.2015

  • Python, если не может исключать элементы из массивов.
  • Прекомпиляторы C не обрабатывают #! или другие строки, начинающиеся с # по мере необходимости.
  • pypreprocessor кажется специфичным для python

Вместо этого используйте обычный m4, например:

ifelse(DEBUG,True,dnl`
  < do some code >
dnl,dnl`
  < do some other code >dnl
')

ifelse(
  M4_CPU,x86_64,`
    < do some code specific for M4_CPU >
',M4_CPU,arm,`
    < do some code specific for M4_CPU >
',M4_CPU,ppc64le,`
    < do some code specific for M4_CPU >
')

ifelse(
  M4_OS,windows,`
    < do some code specific for M4_OS >
  ',M4_OS,linux,`
    < do some code specific for M4_OS >
  ',M4_OS,android,`
    < do some code specific for M4_OS >
')

m4 -D DEBUG=True -D M4_OS=android -D M4_CPU=arm test.py.m4 > test.py

person Torsten Reichert    schedule 22.01.2019