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

Каковы допустимые прецеденты для инструкции `assert` python?

Я часто использую инструкцию python assert для проверки ввода пользователя и сбоя, если мы находимся в коррумпированном состоянии. Я знаю, что assert удаляется при использовании python с флагом -o (оптимизированный). Я лично не запускаю ни одно из моих приложений в оптимизированном режиме, но мне кажется, что я должен держаться подальше от утверждения, как раз в случае.

Сложно писать

assert filename.endswith('.jpg')

чем

if not filename.endswith('.jpg'):
    raise RuntimeError

Это допустимый прецедент для утверждения? Если нет, то какой будет действительный прецедент для оператора python assert?

4b9b3361

Ответ 1

Утверждения должны использоваться для выражения инвариантов или предварительных условий.
В вашем примере вы используете их для проверки неожиданного ввода - и это совершенно другой класс исключений.

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

Ответ 2

Если грациозность невозможна, будьте драматичными

Вот верная версия вашего кода:

if filename.endswith('.jpg'):
    # convert it to the PNG we want
    try:
        filename = convert_jpg_to_png_tmpfile(filename)
    except PNGCreateError:
        # Tell the user their jpg was crap
        print "Your jpg was crap!"

Это действительный случай, ИМХО, когда:

  • Ошибка полностью, 100% смертельна, и иметь дело с ней было бы слишком мрачно, чтобы понять
  • Утверждение должно только терпеть неудачу, если что-то заставляет законы логики меняться

В противном случае, рассмотрите возможность, потому что вы можете увидеть ее.

ASSERT == "Это не должно происходить в действительности, и если это произойдет, мы откажемся"

Конечно, это не то же самое, что

#control should never get here

Но я всегда делаю

#control should never get here
#but i'm not 100% putting my money where my mouth
#is
assert(False)

Таким образом, я получаю хорошую ошибку. В вашем примере я бы использовал версию if и преобразовал файл в jpg!

Ответ 3

Действителен. Утверждение является официальным выражением о состоянии вашей программы.

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

Другой пример.

def fastExp( a, b ):
    assert isinstance(b,(int,long)), "This algorithm raises to an integer power"
    etc.

Еще один. Окончательное утверждение немного глупо, поскольку оно должно быть доказано.

# Not provable, essential.
assert len(someList) > 0, "Can't work with an empty list."
x = someList[]
while len(x) != 0:
    startingSize= len(x)
    ... some processing ...
    # Provable.  May be Redundant.
    assert len(x) < startingSize, "Design Flaw of the worst kind."

Еще один.

def sqrt( x ):
    # This may not be provable and is essential.
    assert x >= 0, "Can't cope with non-positive numbers"
    ...
    # This is provable and may be redundant.
    assert abs( n*n - x ) < 0.00001 
    return n

Существует множество причин для формальных утверждений.

Ответ 4

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

Вы можете лично никогда не запускать с -o, но что произойдет, если ваш код окажется в более крупной системе, и администратор хочет запустить его с помощью -o?

Возможно, что система будет работать нормально, но есть тонкие ошибки, которые были включены при запуске с помощью -o

Ответ 5

Лично я использую assert для непредвиденных ошибок или вещей, которых вы не ожидаете в реальном мире. Исключения должны использоваться всякий раз, когда речь идет о вводе от пользователя или файла, поскольку они могут быть пойманы, и вы можете сказать пользователю "Эй, я ожидал файл .jpg!"

Ответ 6

В Python Wiki есть отличное руководство по эффективному использованию Assertion.

В ответах выше не нужно уточнять -O-возражение при запуске Python. Цитата на предыдущей странице:

Если Python запущен с параметром -O, тогда утверждения будут удалены и не будут оцениваться.

Ответ 7

Ответ S.Lott - лучший. Но это слишком долго, чтобы просто добавить комментарий к нему, поэтому я добавил его здесь. В любом случае, это как я думаю об утверждении, которое в основном состоит в том, что это просто сокращенный способ сделать #ifdef DEBUG.

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

Выполнение цели целиком внутри кода:

def sqrt(x):
    if x<0:
        raise ValueError, 'sqrt requires positive numbers'
    root = <do stuff>
    return root

def some_func(x):
    y = float(raw_input('Type a number:'))
    try:
        print 'Square root is %f'%sqrt(y)
    except ValueError:
        # User did not type valid input
        print '%f must be a positive number!'%y

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

Однако существует штраф за исполнение. Представьте код следующим образом:

def sqrt(x):
    if x<=0:
        raise ValueError, 'sqrt requires positive numbers'
    root = <do stuff>
    return root

def some_func(maxx=100000):
    all_sqrts = [sqrt(x) for x in range(maxx)]
    i = sqrt(-1.0)
    return(all_sqrts)

Теперь эта функция будет вызывать sqrt 100k раз. И каждый раз sqrt собирается проверить, имеет ли значение >= 0. Но мы уже знаем, что это действительно, из-за того, как мы генерируем эти числа - эти дополнительные действительные проверки просто теряют время выполнения. Разве было бы неплохо избавиться от них? И тогда там один, который бросит ValueError, и поэтому мы поймаем его и поймем, что допустили ошибку. Я пишу свою программу, полагаясь на подфункцию, чтобы проверить меня, и поэтому я просто беспокоюсь о восстановлении, когда она не работает.

Вторая школа мысли заключается в том, что вместо входных данных проверки целевой функции вы добавляете ограничения для определения и требуете, чтобы вызывающий пользователь обеспечивал ее вызовом с достоверными данными. Функция promises, которая с хорошими данными, вернет то, что говорит ее контракт. Это позволяет избежать всех этих проверок, поскольку вызывающий абонент знает намного больше о отправляемых данных, чем целевая функция, откуда она взяла и какие ограничения она имеет по своей сути. Конечным результатом этого являются кодовые контракты и аналогичные структуры, но первоначально это было просто по соглашению, т.е. В дизайне, как и комментарий ниже:

# This function valid if x > 0
def sqrt(x):
    root = <do stuff>
    return root

def long_function(maxx=100000):
    # This is a valid function call - every x i pass to sqrt is valid
    sqrtlist1 = [sqrt(x) for x in range(maxx)]
    # This one is a program error - calling function with incorrect arguments
    # But one that can't be statically determined
    # It will throw an exception somewhere in the sqrt code above
    i = sqrt(-1.0)

Конечно, ошибки происходят, и контракт может получить нарушения. Но до сих пор результат примерно такой же - в обоих случаях, если я вызываю sqrt (-1.0), я получу исключение внутри самого кода sqrt, могу подойти к стеку исключений и выяснить, где моя ошибка.

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

Так зачем утверждать? Было бы неплохо иметь что-то, с помощью которого мы могли бы получить информацию об отладке с близкого расстояния до отказа, пока мы тестируем и доказываем наши контракты. Это почти точно так же, как и первая форма - в конце концов, он делает то же самое сравнение, но синтаксически опережает и немного более специализирован для проверки контракта. Дополнительным преимуществом является то, что после того, как вы разумно уверены, что ваша программа работает, и оптимизируют и ищут более высокую производительность по сравнению с возможностью отладки, все эти теперь избыточные проверки могут быть удалены.

Ответ 8

Линия Botton:

  • assert, а его семантика - наследие более ранних языков.
  • Резкий сдвиг фокуса Python доказал, что традиционный случай использования не имеет значения.
  • Никаких других случаев использования официально не было предложено на момент написания этой статьи, хотя есть идеи, чтобы перевести его на проверку общего назначения.
  • Его можно использовать (и действительно использовать) как проверку общего назначения, как сейчас, если вы в порядке с ограничениями.

Предполагаемый прецедент, из которого были изначально созданы операторы assert:

  • , чтобы сделать проверки здравомыслия релевантными, но считающимися слишком дорогими или ненужными для использования в производстве.
    • например. это могут быть входные проверки для внутренних функций, проверяет, что free() передан указатель, который ранее был получен из malloc(), и такие, проверки целостности внутренней структуры после нетривиальных операций с ними и т.д.

Это было большим делом в старые времена и все еще в средах, предназначенных для производительности. Что вся причина его семантики, скажем, в С++/С#:

  • Выключение в выпуске
  • Прекращение программы немедленно, без порицания и как можно громче.

Python, однако, сознательно и намеренно жертвует эффективностью кода для производительности программиста (верьте или нет, я недавно получил 100-кратное ускорение, поместив некоторый код из Python в Cython - даже не отключив пограничные проверки!). Код Python работает в "безопасной" среде, так как вы не можете полностью "разбить" ваш процесс (или всю систему) на неизведанный segfault/BSoD/bricking - худшее, что вы получите, - это необработанное исключение с нагрузкой отладочной информации прилагается, все изящно представлено вам в читаемой форме.

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

Кроме того, Python оказывает сильное влияние на предоставление источников во все времена (прозрачная компиляция, исходные строки в трассировке, отладочный отладчик, ожидающий их рядом с .pyc для использования) очень сильно размывает грань между "разработкой" и "использование" (одна из причин, почему setuptools "автономный .egg создал обратную реакцию - и почему pip всегда устанавливает их распакованными: если один из них упакован, источник уже недоступен и проблемы с ним - диагностируются).

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

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