Подтвердить что ты не робот

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

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

 #ifdef MACRO

 controlled text

 #endif /* MACRO */

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

4b9b3361

Ответ 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 устанавливает значения переменных и внутренние скрипты соответственно. Независимо от того, имеет ли это смысл, конечно, будет зависеть от вашего конкретного варианта использования.

Ответ 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

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