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

  • Есть ли проблема обслуживания производительности или кода при использовании 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, возникает исключение?

Ответ 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
ничего не получит печать