Лучшая практика для Python assert





  1. есть ли проблема производительности или обслуживания кода с использованием assert как часть стандартного кода вместо того, чтобы использовать его только для отладки?



    и



    assert x >= 0, 'x is less than zero'


    лучше или хуже, чем



    if x < 0:
    raise Exception, 'x is less than zero'


  2. кроме того, есть ли способ установить бизнес-правило, как if x < 0 raise error это всегда проверяется без try/except/finally Итак, если в любое время по всему коду x меньше 0 возникает ошибка, например, если вы установили assert x < 0 в самом начале функции, в любом месте внутри функции, где x становится меньше 0 исключение?


627   13  

13 ответов:

чтобы иметь возможность автоматически выдавать ошибку, когда 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

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

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


например, если вы пишете функцию для чтения из файла конфигурации в dict, неправильное форматирование в файле должно вызвать ConfigurationSyntaxError, а assert что ты не собираешься возвращаться None.


в вашем примере, если x - это значение, установленное через пользовательский интерфейс или из внешнего источника, исключение лучше всего.

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

операторы"assert" удаляются при оптимизации компиляции. Так что, да, есть как производительность, так и функциональные различия.

текущий генератор кода не выдает код для оператора assert, когда оптимизация запрашивается во время компиляции. - Python 2.6.4 Docs

Если вы используете assert чтобы реализовать функциональность приложения, а затем оптимизировать развертывание в производство, вы будете страдать от "но-это-работает-в-dev" дефекты.

посмотреть PYTHONOPTIMIZE и -О-ОО

четыре цели assert

затем assert и четыре роли:

  1. сообщите Алисе, Бернду, Карлу и Дафне, что ожидает ваш код.
    Предположим у вас есть метод, который обрабатывает список кортежей и логика программы может сломаться, если эти кортежи не являются неизменными:

    def mymethod(listOfTuples):
        assert(all(type(tp)==tuple for tp in listOfTuples))
    

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

  2. сообщите компьютеру, что ожидает ваш код.
    assert обеспечивает правильное поведение от абонентов из кода. Если ваш код вызывает код Алисы и Бернда вызывает ваш, тогда без assert, если программа аварийно завершает работу в коде Alices, Бернд мог бы предположить, что это была вина Элис, Алиса расследует и может предположить, что это была ваша вина, вы исследуете и говорите Бернду, что это было на самом деле его. Много работы потеряно.
    С утверждениями, кто бы ни получил неправильный звонок, они быстро смогут увидеть, что это было это их вина, а не твоя. Элис, Бернд и вы все выиграли. Экономит огромное количество времени.

  3. сообщите читателям вашего кода (включая себя), что ваш код достиг в какой-то момент.
    Предположим, у вас есть список записей и каждый из них может быть чистым (что хорошо) или это может быть smorsh, trale, gullup или twinkled (которые все не приемлемы). Если это сморш, значит, не Морш; если Трэйл, значит, балудоед.; если это галлап, он должен быть рысью (а затем, возможно, и шагал); если он мерцает, то должен мерцать снова, кроме четвергов. Вы понимаете: это сложный материал. Но конечным результатом является (или должно быть), что все записи были чистыми. Правильная вещь (TM), чтобы сделать, это суммировать эффект ваш цикл очистки как

    assert(all(entry.isClean() for entry in mylist))
    

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

  4. сообщите компьютеру, что ваш код достиг в какой-то момент.
    Если вы когда-нибудь забудете шагать по записи, нуждающейся в нем после рыси, элемент assert спасет ваш день и избежать ваш код разбивает дорогую Дафну гораздо позже.

на мой взгляд, assertдве цели документации (1 и 3) и защиты (2 и 4) одинаково ценны.
Информирование людей может быть даже больше ценнее, чем информирование компьютер потому что это может предотвратить те самые ошибки assert стремится поймать (в случае 1) и много последующих ошибок в любом случае.

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

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

class XLessThanZeroException(Exception):
    pass

def CheckX(x):
    if x < 0:
        raise XLessThanZeroException()

def foo(x):
    CheckX(x)
    #do stuff here

другая проблема заключается в том, что использование assert для обычной проверки состояния заключается в том, что это затрудняет отключение утверждений отладки с помощью флага-O.

как было сказано ранее, утверждения должны использоваться, когда ваш код никогда не должен достигать точки, то есть там есть ошибка. Вероятно, самая полезная причина, по которой я могу использовать утверждение, - это инвариант/pre/postcondition. Это то, что должно быть истинным в начале или конце каждой итерации цикла или функции.

например, рекурсивная функция (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

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

слово английского языка assert здесь используется в смысле клянусь,подтвердить,подтвердить. Это не значит "проверить" или "должно быть". Это значит, что вы как кодер делает показания здесь:

# 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

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

и есть проблемы с производительностью?

  • не забудьте "заставьте его работать первым, прежде чем заставить его работать быстро".
    Очень немногие проценты любой программы обычно имеют отношение к ее скорости. Вы всегда можете выгнать или упростить 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!
    

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

есть фреймворк под названием JBoss слюнки текут для java, который выполняет мониторинг времени выполнения для утверждения бизнес-правил, что отвечает на вторую часть вашего вопроса. Однако я не уверен, есть ли такая структура для python.

утверждение-это проверка -
1. действительное условие,
2. действительное утверждение,
3. истинная логика;
исходного кода. Вместо того, чтобы провалить весь проект он дает сигнал, что что-то не подходит в вашем исходном файле.

в Примере 1, так как переменная ' str ' не является нулем. Поэтому никаких утверждений или исключений не возникает.

Пример 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
ничто не получит печать

в IDE, таких как PTVS, PyCharm, Wing assert isinstance() операторы могут быть использованы для включения завершения кода для некоторых неясных объектов.

Если вы имеете дело с унаследованным кодом, который полагается на assert для правильной работы, даже если не должен, затем добавление следующего кода является быстрым решением, пока вы не найдете время для рефакторинга:

try:
    assert False
    raise Exception('Python Assertions are not working. This tool relies on Python Assertions to do its job. Possible causes are running with the "-O" flag or running a precompiled (".pyo" or ".pyc") module.')
except AssertionError:
    pass

Comments

    Ничего не найдено.