Делаем ваши функции более читабельными и надежными
Когда-то унаследованная мудрость заключалась в том, что функции должны иметь один и только один оператор return
. Это эволюционировало, чтобы покончить с безумием операторов goto
, но менее актуально для современных языков программирования, а защитные предложения представляют собой прекрасную современную альтернативу этой догме.
Возьмем простой пример. Скажем, вы хотите написать функцию для вычисления площади квадрата. Простейшая форма может быть:
def calculate_area(width): return width * width
Ах, но вы, вероятно, захотите добавить некоторую обработку ошибок, чтобы убедиться, что ширина, которую вы передаете, является допустимым числом. Вы можете получить функцию, которая выглядит так:
def calculate_area(width): if width >= 0: area = width * width else: area = 0 return area
И это нормально. В этом нет ничего плохого. Но как только функции начинают становиться хоть немного сложнее, все становится еще уродливее. Скажем, вместо этого мы хотим вычислить площадь прямоугольника:
def calculate_area(width, height): if width >= 0: if height >= 0: area = width * height else: area = 0 else: area = 0 return area
Что эти вложенные операторы if/else пытаются сделать, так это проверить, что входные аргументы имеют смысл, и если они имеют, то продолжить. Проблема с проверкой правильности и подобными действиями заключается в том, что в конечном итоге вы получаете глубоко вложенную логику управления, которой трудно следовать.
Введите защитную оговорку
Есть гораздо более элегантный способ написания кода в двух приведенных выше примерах. Для первого примера вычисления площади квадрата:
def calculate_area(width): if width < 0: return 0 return width * width
Это может выглядеть так же, как и в первом примере выше, но на самом деле он короче — проверяя неверный ввод и затем немедленно выходя из функции, нам удалось сократить одну строку кода.
Это называется «защитой» или «защитным предложением» — так потому, что оно «защищает» функцию. Если входные данные не совсем то, что мы ожидаем, мы немедленно уходим и не делаем никакой дальнейшей работы.
Этот код также быстрее, потому что вместо того, чтобы оценивать оба блока if/else, мы имеем дело только с одним блоком if. У компилятора меньше областей действия и меньше кода для интерпретации.
Увеличение масштаба
Однако прелесть этого в том, что он хорошо масштабируется по мере увеличения количества аргументов. Возьмем наш второй игрушечный пример, переработанный для использования защитных предложений.
def calculate_area(width, height): if width < 0: return 0 if height < 0: return 0 return width * height
Красивый! Теперь наша условная логика стала менее глубокой и запутанной.
Проверьте правильность ввода, затем выйдите
Шаблон защитных предложений включает проверку того, не нарушает ли входные данные требуемые критерии, а затем досрочный выход из функции, если это так.
Предложения Guard обеспечивают более простую и удобочитаемую логику управления, чем альтернативы, в дополнение к более быстрому выполнению.
Исключения по возврату
Это не обязательно должно быть с операторами возврата. С тем же успехом вы могли бы создать исключение на языках, которые его поддерживают.
С использованием исключений наш более сложный пример может выглядеть так:
def calculate_area(width, height): if width < 0: raise ValueError("Width cannot be less than zero") if height < 0: return ValueError("Height cannot be less than zero") return width * height
На самом деле, генерировать исключения, когда входные данные не соответствуют вашим ожиданиям, часто (хотя и не всегда) лучше, чем возвращать значения по умолчанию или разумные значения.
Сначала обработайте исключительный случай
На самом деле мы можем переписать сложный пример выше, используя этот шаблон, чтобы сделать его более читабельным:
def calculate_area(width, height): if width < 0 or height < 0: area = 0 else: area = width * height return area
Технически это не использование защитных предложений, но используется тот же общий шаблон, чтобы сделать код намного более элегантным — сначала обработать исключительный случай. Если во входных данных есть что-то исключительное, разберитесь с этим. Если во входных данных нет ничего исключительного и все выглядит нормально, выполните «счастливый путь».
Единственная реальная разница между приведенным выше кодом и кодом, использующим защитные предложения, заключается в том, что мы опускаем оператор «else» в пользу раннего возврата.
Реальное преимущество использования защитных предложений и раннего возврата заключается в том, что становится ясно, каков счастливый путь функции — он будет находиться в самой внешней области внутри функции.
Вы ожидаете, что этот код будет выполнен, если не произойдет что-то странное, тогда как не сразу очевидно, что то, что находится внутри оператора «else», — это то, что вы ожидаете нормально выполнить.
Заключительные мысли
Обрабатывая исключительный случай в первую очередь, вы можете существенно снизить сложность и улучшить читаемость и скорость вашего кода при проверке входных данных. Использование защитных предложений продвинет вас на один шаг вперед, чтобы прояснить счастливый путь функции и немного ускорить выполнение кода.