Отключить утверждения в Python

90

Как отключить утверждения в Python?

То есть, если утверждение терпит неудачу, я не хочу, чтобы оно бросало AssertionError, а чтобы оно продолжалось.

Как я могу это сделать?

Клаудиу
источник

Ответы:

75

Как отключить утверждения в Python?

Есть несколько подходов, которые влияют на один процесс, среду или одну строку кода.

Я демонстрирую каждый.

На весь процесс

Использование -Oфлага (заглавная O) отключает все утверждения утверждения в процессе.

Например:

$ python -Oc "assert False"

$ python -c "assert False"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
AssertionError

Обратите внимание, что под отключением я подразумеваю, что он также не выполняет следующее за ним выражение:

$ python -Oc "assert 1/0"

$ python -c "assert 1/0"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero

Для окружающей среды

Вы также можете использовать переменную среды, чтобы установить этот флаг.

Это повлияет на каждый процесс, который использует или наследует среду.

Например, в Windows установка, а затем очистка переменной среды:

C:\>python -c "assert False"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
AssertionError
C:\>SET PYTHONOPTIMIZE=TRUE

C:\>python -c "assert False"

C:\>SET PYTHONOPTIMIZE=

C:\>python -c "assert False"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
AssertionError

То же самое в Unix (с использованием set и unset для соответствующей функциональности)

Единая точка в коде

Вы продолжаете свой вопрос:

если утверждение терпит неудачу, я не хочу, чтобы оно выдавало AssertionError, а чтобы оно продолжалось.

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

if False:
    assert False, "we know this fails, but we don't get here"

или вы можете поймать ошибку утверждения:

try:
    assert False, "this code runs, fails, and the exception is caught"
except AssertionError as e:
    print(repr(e))

который печатает:

AssertionError('this code runs, fails, and the exception is caught')

и вы будете продолжать с того момента, как вы обработали AssertionError.

Ссылки

Из в assertдокументации :

Утверждение вроде этого:

assert expression #, optional_message

Эквивалентно

if __debug__:
    if not expression: raise AssertionError #(optional_message)

А также,

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

и далее

Назначение на __debug__незаконное. Значение встроенной переменной определяется при запуске интерпретатора.

Из документации по использованию:

-O

Включите базовую оптимизацию. Это изменяет расширение имени файла для скомпилированных (байт-кода) файлов с .pyc на .pyo. См. Также PYTHONOPTIMIZE.

а также

PYTHONOPTIMIZE

Если для него задана непустая строка, это эквивалентно указанию -Oпараметра. Если установлено целое число, это эквивалентно указанию -Oнескольких раз.

Аарон Холл
источник
можно ли пропустить код, который не работает в случае «Единственная точка в коде»? Я попытался установить __debug__значение "Ложь", но это запрещено.
Matthijs
1
@Matthijs вы можете либо убедиться, что поток управления не достигает его (например if False: assert False), либо вы можете поймать ошибку утверждения. Это ваш выбор. Обновлен ответ, чтобы ответить на ваш вопрос.
Аарон Холл
Спасибо за ответ, но еще не совсем то, о чем я думал. Я хотел бы отключить утверждает внутри функции во время выполнения, в идеале с какой - то менеджер контекста: утверждение оценивается: foo()и коммутационные утверждения от: with skip_assertion(): foo(). Преимущество этого в том, что мне не нужно добавлять еще один флаг в функцию
Matthijs
2
Вы можете переписать байт-код функции, переписать AST или переписать саму функцию. (вручную или программно, для любого). Переписывание AST, вероятно, было бы наиболее надежным подходом («просто» заменять Assertобъекты Passобъектами). Диспетчер контекста не будет работать напрямую для этого, но у вас может быть какой-то механизм, который таким образом использует оформленные функции. Тем не менее, я не рекомендую это. Я подозреваю, что причина вашего желания это сделать - вы вызываете код, который не контролируете, и получаете AssertionErrors. Если да, вероятно, вам нужно найти другое исправление.
Аарон Холл
59

Вызов Python с флагом -O:

test.py:

assert(False)
print 'Done'

Выход:

C:\temp\py>C:\Python26\python.exe test.py
Traceback (most recent call last):
  File "test.py", line 1, in <module>
    assert(False)
AssertionError

C:\temp\py>C:\Python26\python.exe -O test.py
Done
Марк Рушаков
источник
8
Assert - это не функция, поэтому парные скобки излишни.
Аарон Холл
15

Оба из двух уже предоставленных ответов действительны (вызовите Python с помощью одного -Oили -OOв командной строке).

Вот разница между ними:

  • -OВключите базовую оптимизацию. Это изменяет расширение имени файла для скомпилированных (байт-кода) файлов с .pyc на .pyo.

  • -OOОткажитесь от строк документации в дополнение к -Oоптимизации.

(Из документации Python )

Майкл Карри
источник
7

Использование python -O:

$ python -O
>>> assert False
>>> 
Джон Милликин
источник
3

Вы НЕ должны отключать (большинство) утверждений. Они ловят непредвиденные ошибки, когда внимание сосредоточено на другом. См. Правило 5 в «Степень десяти» .

Вместо этого защитите некоторые дорогостоящие проверки утверждений чем-то вроде:

import logging
logger = logging.getLogger(__name__)

if logger.getEffectiveLevel() < logging.DEBUG:
    ok = check_expensive_property()
    assert ok, 'Run !'

Один из способов сохранить важные утверждения и позволить assertоптимизировать операторы - это поднять их в операторе выбора:

if foo_is_broken():
    raise AssertionError('Foo is broken!')
Иоаннис Филиппидис
источник
1
// Проблема, однако, в том, что оператор все еще увеличивает цикломатическую сложность, а остальное должно обрабатывать обработка ошибок?
Натан Басанезе
1
Утверждения, которые будут охраняться, как указано выше, являются дорогостоящими вызовами, которые значительно замедляют выполнение. Для некоторых алгоритмов проверки такого рода могут занимать на порядки больше времени, чем проверка всей программы. Подумайте о запуске наивной, но более простой реализации (чтобы с меньшей вероятностью содержать ошибки) того же алгоритма для проверки правильности. Или проверка путем исчерпывающего перебора того, о чем не может быть и речи о нормальной работе.
Иоаннис Филиппидис
Я не вижу особых проблем с удобочитаемостью, потому что такой оператор не добавляет вложенности в код. Извлечение его как вызова функции может убрать его с пути, если это проблема (и я ожидаю, что такой рефакторинг должен снизить цикломатическую сложность). В любом случае цикломатическая сложность не должна влиять на проверки безопасности.
Иоаннис Филиппидис
2

Запуск в оптимизированном режиме должен сделать это:

python -OO module.py
ТуманПтица
источник