-
Есть ли проблема обслуживания производительности или кода при использовании
assert
как части стандартного кода вместо использования его только для целей отладки?Есть
assert x >= 0, 'x is less than zero'
лучше или хуже
if x < 0: raise Exception, 'x is less than zero'
-
Также существует ли способ установить бизнес-правило типа
if x < 0 raise error
, которое всегда проверяется безtry/except/finally
, поэтому, если в любой момент кодаx
меньше 0, возникает ошибка, например, если вы устанавливаетеassert x < 0
в начале функции, где-нибудь внутри функции, гдеx
становится меньше 0, возникает исключение?
Лучшая практика для Python
Ответ 1
Уметь автоматически бросать ошибку, когда x становится меньше нуля во всей функции. Вы можете использовать дескрипторы классов. Вот пример:
class LessThanZeroException(Exception):
pass
class variable(object):
def __init__(self, value=0):
self.__x = value
def __set__(self, obj, value):
if value < 0:
raise LessThanZeroException('x is less than zero')
self.__x = value
def __get__(self, obj, objType):
return self.__x
class MyClass(object):
x = variable()
>>> m = MyClass()
>>> m.x = 10
>>> m.x -= 20
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "my.py", line 7, in __set__
raise LessThanZeroException('x is less than zero')
LessThanZeroException: x is less than zero
Ответ 2
Активы должны использоваться для проверки условий, которые никогда не должны происходить. Цель состоит в том, чтобы рано сбой в случае поврежденного состояния программы.
Исключения должны использоваться для ошибок, которые могут возникнуть, и вы должны почти всегда создавать свои собственные классы исключений.
Например, если вы пишете функцию для чтения из файла конфигурации в dict
, неправильное форматирование в файле должно поднять ConfigurationSyntaxError
, а вы можете assert
, что вы не собираетесь return None
.
В вашем примере, если x
- это значение, заданное через пользовательский интерфейс или из внешнего источника, лучше всего сделать исключение.
Если x
устанавливается только вашим собственным кодом в той же программе, перейдите к утверждению.
Ответ 3
Операторы"assert" удаляются, когда оптимизация компиляции. Итак, да, есть как функциональные, так и функциональные различия.
Текущий генератор кода не генерирует код для оператора assert при запросе оптимизации во время компиляции. - Python 2.6.4 Docs
Если вы используете assert
для реализации функциональности приложения, а затем оптимизируйте развертывание для производства, вы будете сталкиваться с дефектами "but-it-works-in-dev".
См. PYTHONOPTIMIZE и - O -OO
Ответ 4
Предположим, вы работаете над 200 000 строк кода с четырьмя коллегами Алисой, Берндом, Карлом и Дафной. Они называют ваш код, вы называете его кодом.
Затем assert
имеет четыре роли:
-
Сообщите Алису, Бернду, Карлу и Дафни, что ожидает ваш код.
Предположим, что у вас есть метод, который обрабатывает список кортежей, и логика программы может ломаться, если эти кортежи не являются неизменяемыми:def mymethod(listOfTuples): assert(all(type(tp)==tuple for tp in listOfTuples))
Это более достоверно, чем эквивалентная информация в документации и намного легче поддерживать.
-
Сообщите компьютеру, что ожидает ваш код.
assert
обеспечивает правильное поведение от вызывающих пользователей вашего кода. Если ваш код вызывает Alices и код Bernd, то безassert
, если программа выйдет из строя в коде Alices, Бернд мог предположить, что это была ошибка Алисы, Алиса расследует и может предположить, что это была ваша вина, вы расследуете и говорите Бернду, что это было на самом деле его. Много работы потеряно.
С утверждениями, кто бы ни ошибался, они быстро смогут увидеть, что это было их вина, а не ваша. Алиса, Бернд, и вы все выиграете. Экономит огромное количество времени. -
Сообщите читателям вашего кода (включая себя), что ваш код достиг в какой-то момент.
Предположим, у вас есть список записей, и каждый из них может быть чистым (что хорошо) или это может быть smorsh, trale, gullup или мерцание (которые все неприемлемы). Если он smorsh, он должен быть без морщин; если он бьет, он должен быть балудован; если это gullup, он должен быть рысью (а затем, возможно, и дальше); если он мерцает, он должен снова мерцать, кроме четверга. Вы получаете идею: это сложный материал. Но конечным результатом является (или должно быть), что все записи чистые. Правильная вещь (TM), которую нужно сделать, - суммировать эффект вашего очищающая петля какassert(all(entry.isClean() for entry in mylist))
Эти утверждения сохраняют головную боль для всех, кто пытается понять что именно это делает замечательный цикл. И самый частый из этих людей, скорее всего, будет собой.
-
Сообщите компьютеру, что ваш код достиг в какой-то момент.
Если вы когда-нибудь забудете пропустить запись, требующую ее после рыси,assert
сохранит ваш день и избежит того, чтобы ваш код разрывает дорогую Дафну намного позже.
На мой взгляд, assert
две цели документации (1 и 3) и
(2 и 4) одинаково ценны.
Информирование людей может быть даже более ценным, чем информирование компьютера
потому что он может предотвратить те самые ошибки, которые стремится assert
уловить (в случае 1)
и множество последующих ошибок в любом случае.
Ответ 5
В дополнение к другим ответам, сами утверждают, что выбрасывают исключения, но только AssertionErrors. С утилитарной точки зрения утверждения не подходят, когда вам нужен мелкозернистый контроль над исключениями, которые вы улавливаете.
Ответ 6
Единственное, что действительно неверно в этом подходе, состоит в том, что сложно сделать очень описательное исключение, используя утверждения assert. Если вы ищете простой синтаксис, помните, что вы также можете сделать что-то вроде этого:
class XLessThanZeroException(Exception):
pass
def CheckX(x):
if x < 0:
raise XLessThanZeroException()
def foo(x):
CheckX(x)
#do stuff here
Другая проблема заключается в том, что использование assert для нормальной проверки состояния заключается в том, что это затрудняет отключение утверждений отладки с использованием флага -O.
Ответ 7
Как уже было сказано, утверждения должны использоваться, когда ваш код НЕ ДОЛЖЕН когда-либо доходить до точки, то есть там есть ошибка. Вероятно, наиболее полезной причиной, по которой я могу использовать утверждение, является инвариантное/предварительное условие. Это то, что должно быть истинным в начале или конце каждой итерации цикла или функции.
Например, рекурсивная функция (2 отдельных функции, поэтому 1 обрабатывает плохой ввод, а другой обрабатывает плохой код, из-за чего трудно отличить рекурсию). Это сделало бы очевидным, если бы я забыл написать инструкцию if, что пошло не так.
def SumToN(n):
if n <= 0:
raise ValueError, "N must be greater than or equal to 0"
else:
return RecursiveSum(n)
def RecursiveSum(n):
#precondition: n >= 0
assert(n >= 0)
if n == 0:
return 0
return RecursiveSum(n - 1) + n
#postcondition: returned sum of 1 to n
Эти инварианты цикла часто могут быть представлены с утверждением.
Ответ 8
Что касается "Есть ли проблема с производительностью?":
-
Пожалуйста, не забудьте "заставить его работать прежде, чем заставить его работать быстро".
Очень мало процентов любой программы, как правило, имеют отношение к ее скорости. Вы всегда можете выпустить или упроститьassert
, если это когда-либо докажет быть проблемой производительности - и большинство из них никогда не будет. -
Будьте прагматичны:
Предположим, что у вас есть метод, который обрабатывает непустой список кортежей, и логика программы прерывается, если эти кортежи не являются неизменяемыми. Вы должны написать:def mymethod(listOfTuples): assert(all(type(tp)==tuple for tp in listOfTuples))
Это, вероятно, хорошо, если ваши списки имеют тенденцию к десяти долгам, но это может стать проблемой, если у них есть миллион записей. Но вместо того, чтобы полностью отказаться от этой ценной проверки, вы могли бы просто понизите его до
def mymethod(listOfTuples): assert(type(listOfTuples[0])==tuple) # in fact _all_ must be tuples!
который дешев, но, скорее всего, поймает 97% от фактических ошибок программы.
Ответ 9
Здесь есть фреймворк под названием JBoss Drools для java, который выполняет мониторинг времени выполнения для утверждения бизнес-правил, что отвечает на вторую часть вашего вопроса. Однако я не уверен, есть ли такая платформа для python.
Ответ 10
В среде IDE, такой как PTVS, инструкции PyCharm, Wing assert isinstance()
могут использоваться для включения завершения кода для некоторых нечетких объектов.
Ответ 11
Слово английского языка assert здесь используется в смысле ругаться, утверждать, avow. Это не означает "проверка" или "должно быть". Это означает, что вы, как кодер, делаете присяжный оператор здесь:
# I solemnly swear that here I will tell the truth, the whole truth,
# and nothing but the truth, under pains and penalties of perjury, so help me FSM
assert answer == 42
Если код верен, запрет отклонения от одного события, аппаратные сбои и т.д. no assert никогда не выйдет. Вот почему поведение программы для конечного пользователя не должно быть затронуто. В частности, утверждение не может не справиться даже в исключительных программных условиях. Этого никогда не бывает. Если это произойдет, программист должен быть заблокирован для него.
Ответ 12
Подтверждение - проверить -
1. действительное условие,
2. действительное выражение,
3. истинная логика;
исходного кода. Вместо того, чтобы проваливать весь проект, он дает сигнал о том, что что-то не подходит в исходном файле.
В примере 1, поскольку переменная 'str' не является nul. Поэтому никаких утверждений или исключений не возникает.
Пример 1:
#!/usr/bin/python
str = 'hello Pyhton!'
strNull = 'string is Null'
if __debug__:
if not str: raise AssertionError(strNull)
print str
if __debug__:
print 'FileName '.ljust(30,'.'),(__name__)
print 'FilePath '.ljust(30,'.'),(__file__)
------------------------------------------------------
Output:
hello Pyhton!
FileName ..................... hello
FilePath ..................... C:/Python\hello.py
В примере 2 var 'str' равен nul. Таким образом, мы спасаем пользователя от опережения ошибочной программы оператором assert.
Пример 2:
#!/usr/bin/python
str = ''
strNull = 'NULL String'
if __debug__:
if not str: raise AssertionError(strNull)
print str
if __debug__:
print 'FileName '.ljust(30,'.'),(__name__)
print 'FilePath '.ljust(30,'.'),(__file__)
------------------------------------------------------
Output:
AssertionError: NULL String
В тот момент, когда мы не хотим отлаживать и реализовывать проблему утверждения в исходном коде. Отключить флаг оптимизации
python -O assertStatement.py
ничего не получит печать