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

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

 #ifdef MACRO

 controlled text

 #endif /* MACRO */

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

7 ответов


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

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

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

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


показать как!--4--> работает, рассмотрим эти две функции:

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) - это обычный оператор, который выполняется во время выполнения, а не во время компиляции (разбора), поэтому его можно поместить внутри обычного python if. Довольно распространенным является, например, наличие import внутри try блок, который предоставит альтернативные определения для некоторых функций, если в системе нет определенной дополнительной библиотеки Python.

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

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


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

Edit: на самом деле, похоже, что верхний ответ на этот вопрос будет более освещающим: как бы вы сделали эквивалент директив препроцессора в Python?

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


вот пример, который я использую для различения 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 """

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


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

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

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/