Найти тему
Simple Prog

Прикрой свой з** инструкциями assert. Шаблоны чистого Python

Оглавление

Иногда по-настоящему полезное функциональное средство языка привлекает меньше внимания, чем оно того заслуживает. По некоторым причинам это именно то, что произошло со встроенной в Python инструкцией assert.

Для тех, кто вообще не знает, что такое assert и в чем ее прелесть, дам несколько ответов. По своей сути инструкция asssert представляет собой средство отладки, которое проверяет условие. Если условие истинно, то ничего не происходит и ваша программа работает дальше. Но если результат вычисления дает результат ложно, то вызывается исключение AssertionError с возможным сообщением об ошибке.

assert в Python - пример

Вот простой пример, чтобы дать вам понять, где инструкции assert могут пригодиться. Предположим, вы создаете интернет-магазин с помощью Python. Вы работаете над добавлением в систему функциональности скидочного купона, и в итоге пишете следующую функцию apply_discount:

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

Давайте убедимся, что эта функция действительно работает, как задумано. Создадим пример товара - пару симпатичных туфель по цене 149,00 $:

shoes = {'имя': 'Модные туфли', 'цена': '14900'}

Я домножил на 100, чтобы избежать проблем с округлением цены. Итак, если к этим туфлям мы применим 25%-ую скидку, то ожидаемо придем к отпускной цене 111, 75 $.

Все сработало отлично. Давайте теперь попробуем использовать 200%-ую скидку, которая вынудит нас отдать деньги покупателю:

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

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

Почему просто не применить обычное исключение?

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

Дело в том, что инструкция assert предназначена для того, чтобы сообщать разработчикам о неустранимых ошибках в программе. Она не предназначена для того, чтобы сигнализировать об ожидаемых ошибочных условиях, типа "Файл не найден" и тп.

Если ваша программа бездефектна, то эти условия никогда не возникнут. Но если же она возникают, то программа завершится аварийно с исключением AssertionError.

Синтаксис инструкции Python assert

Итак, в соответствии с документацией Python, синтаксис assert выглядит так:

инструкция_assert ::= "assert" выражение1 [" , " выражение2]

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

Предостережение №1: не используйте инструкции assert для проверки данных

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

И снова пример с интернет-магазином. Где-то среди программного кода есть функция, которая удаляет товар по запросу пользователя. Поскольку вы только что узнали об assert, вам не терпится ее применить, ведь так?) В общем, вы пишете такую реализацию:

В этом примере трехстрочной функции есть две серьезные проблемы:

  1. Проверка полномочий администратора функцией assert несет в себе опасность. Если утверждения assert отключены в интерпретаторе, то проверка полномочий превращается в нулевую операцию. И поэтому теперь любой пользователь может удалять товары, что, конечно же, очень плохо
  2. Проверка has_product() пропускается, когда assert отключена. Это означает, что метод get_product() теперь можно вызывать с недопустимыми id товаров, что может привести к более серьезным ошибкам. В худшем случае она может стать началом запуска DDoS-атак.

Каким образом можно избежать таких проблем? Ответ таков: никогда не используйте assert для валидации данных. Вместо этого можно выполнять проверку обычными инструкциями if и при необходимости вызывать исключения:

Теперь наш пример вызывает семантически правильные исключения ValueError и AuthError (которые мы должны определить сами).

Предостережение №2: инструкции assert, которые никогда не дают сбоя.

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

Когда в инструкцию assert в качестве первого аргумента передается кортеж, она всегда возвращает True и по этой причине выполняется успешно.

Например, это утверждение никогда не даст сбой:

assert(1 == 2, 'Это утверждение должно вызвать сбой')

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

Представьте, -что у вас имеется следующее утверждение:

На первый взгляд все приемлемо. Однако он никогда не выловит исключение: assert будет всегда давать истину, независимо от состояния переменной counter. И в чем же дело? А в том, что оно подверждает истинность объекта-кортежа.

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

Ключевые выводы

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