Этот блог пост направлен на демистифицировать декораторы Python для начинающих с глупыми требованиями.
Концепция: функции высшего порядка
Википедия описывает Функции высшего порядка следующее:
В математике и информатике функция более высокого порядка является функцией, которая делает по меньшей мере одно из следующих действий:
- принимает один или несколько функций в качестве аргументов
- Возвращает функцию в качестве результата.
карта
Это простой пример функции более высокого порядка, которая соответствует первому состоянию, указанному выше. карта
Принимает функцию в качестве первого аргумента, а именно ее второй аргумент. Функция, которую мы проходим в качестве первого аргумента, вызывается с каждым элементом оригинала, чтобы дать нам новую передачу при применении преобразования. Например:
# You can pass a lambda function as an argument >>> list(map(lambda x: x * 2, [1, 2, 3, 4, 5])) [2, 4, 6, 8, 10] # ...or even a named function >>> def say_hello(name): return f'Hello, {name}' >>> list(map(say_hello, ['John', 'Jane'])) ['Hello, John', 'Hello, Jane']
Теперь давайте перейдем к некоторым требованиям для нашей программы, где мы можем начать использовать функции более высокого порядка.
Требование: Создавать функции, чтобы добавить и умножить два десятичных целых числа
Кажется, простым, давайте упаковывать нашу 1 точечную историю.
Примечание. Для простоты я добавляю некоторые элементарные тесты в одном файле. Вы можете вставить целые фрагменты в файл и запустить его, чтобы убедиться, что никаких утверждений не удается.
def add(first, second): return first + second def multiply(first, second): return first * second if __name__ == '__main__': assert add(3, 4) == 7 assert multiply(3, 4) == 12
Новое требование: для обоих методов, если какой-либо из входов меньше или равен 0, вернуть 0
Да, это не имеет большого смысла Но это то, что требуется!
def is_not_natural_number(number): if number <= 0: return True return False def custom_add(first, second): if any(map(is_not_natural_number, {first, second})): return 0 return first + second def custom_multiply(first, second): if any(map(is_not_natural_number, {first, second})): return 0 return first * second if __name__ == '__main__': assert custom_add(3, 4) == 7 assert custom_multiply(3, 4) == 12 assert custom_add(0, 3) == 0 assert custom_multiply(-1, 3) == 0 assert is_not_natural_number(0) == True assert is_not_natural_number(-1) == True assert is_not_natural_number(1) == False
Все наши тесты проходят, и мы знаем, что функции работают как ожидалось. Тем не менее, есть повторение. Вещи могут быть грязными, если мы просят добавить больше охранников, подобных этому, или если мы просят реализовать больше пользовательских математических средств, таких как вычесть, разделить и т. Д.
Мы можем решить это, создавая функцию более высокого порядка, которая выполняет все общие задачи, а затем вызывает нашу целевую функцию, если требуется. Давайте начнем рефакторинг кода.
Наша собственная функция высшего порядка: custom_math_guard
Давайте создадим функцию под названием custom_math_guard
это делает функцию ФУНК
в качестве первого аргумента, а затем аргументы будут переданы на эту функцию. Функция Guard проверяет вход, чтобы проверить, соответствует ли он нашим требованиям и звонки функция
Только если все выглядит хорошо.
def is_not_natural_number(number): if number <= 0: return True return False def add(first, second): return first + second def multiply(first, second): return first * second def custom_math_guard(func, first, second): if any(map(is_not_natural_number, {first, second})): return 0 return func(first, second) if __name__ == '__main__': # Some tests have not been shown for the sake of brevity assert custom_math_guard(add, 3, 4) == 7 assert custom_math_guard(multiply, 3, 4) == 12 assert custom_math_guard(add, 0, 3) == 0 assert custom_math_guard(multiply, -1, 3) == 0
Мы смогли извлечь состояние нашего охранника в отдельный метод. Теперь мы можем добавить все общие проверки в custom_math_guard
и убедитесь, что наша логика не загрязнена случайными проверками.
Однако это слишком многословным. Мы не хотим, чтобы кодовая база усеялась custom_math_guard (foo, baz, bar)
Если мы сможем избежать этого.
Возвращая функции из функции
Пока что мы говорили только о функциях более высокого порядка, которые принимают функцию в качестве аргумента. Однако они также могут вернуть другую функцию.
Мы можем изменить custom_math_guard
Принять функцию ФУНК
как аргумент, и верните другую функцию returned_from_custom_math_guard
Отказ returned_from_custom_math_guard
имеет доступ к ФУНК
Это было первоначально прошло как аргумент для custom_math_guard
и позвоните, когда требуется. Он также может выполнять наши общие проверки не естественных чисел.
def is_not_natural_number(number): if number <= 0: return True return False def add(first, second): return first + second def multiply(first, second): return first * second def custom_math_guard(func): def returned_from_custom_math_guard(first, second): if any(map(is_not_natural_number, {first, second})): return 0 return func(first, second) return returned_from_custom_math_guard custom_add = custom_math_guard(add) custom_multiply = custom_math_guard(multiply) if __name__ == '__main__': assert custom_add(3, 4) == 7 assert custom_multiply(3, 4) == 12 assert custom_add(0, 3) == 0 assert custom_multiply(-1, 3) == 0
Намного чище, верно? custom_add
просто returned_from_custom_math_guard
который знает, что это должно позвонить в Добавить
Функция, если проходит проверка ввода.
Где декоратор?
Вам может быть интересно, почему название блога пост говорит «Python Decorators упростил», но я говорил обо всем, кроме декораторов. Это потому, что, декораторы не более чем синтаксические сахар для линии custom_add (Добавить)
в фрагменте кода выше. Вместо того, чтобы определить функцию, а затем присвоение результата преобразования в переменную, мы можем просто использовать декоратор, чтобы Python знать, что мы хотим преобразовывать функцию.
def is_not_natural_number(number): if number <= 0: return True return False def custom_math_guard(func): def returned_from_custom_math_guard(first, second): if any(map(is_not_natural_number, {first, second})): return 0 return func(first, second) return returned_from_custom_math_guard @custom_math_guard def custom_add(first, second): return first + second @custom_math_guard def custom_multiply(first, second): return first * second if __name__ == '__main__': assert custom_add(3, 4) == 7 assert custom_multiply(3, 4) == 12 assert custom_add(0, 3) == 0 assert custom_multiply(-1, 3) == 0
Тестовые случаи оставались без изменений, и все они проходят. Таким образом, мы знаем, что наш код делает то же самое, что он сделал раньше. Несколько изменений, которые были сделаны:
- Определить
custom_math_guard
До того, как он используется: интерпретатор Python проходит через нашу строку скрипта по линии сверху вниз, и она бросает ошибку, если она находит декоратор, ссылающийся на функцию, пока она еще не видела. Таким образом, важно заказывать методы в правильной последовательности. Это обычно не проблема, поскольку мы, вероятно, импортируйте декораторы из другого файла/пакета. Поскольку импорт выполняется в верхней части файла, мы можем свободно использовать их где угодно в остальной части файла, не беспокоясь о правильном порядке определений функций. - Мы больше не должны определять функцию
Добавить
а затем назначить результатcustom_math_guard (Добавить)
кcustom_add
Отказ Нам не нужно беспокоиться о том, что кто-то непреднамеренно звонитДобавить
вместоcustom_add
или! У нас есть только одна функция, которая делает то, что мы хотим, чтобы это сделать.
Закрытие заметок
Декораторы мощные и могут радикально изменять поток выполнения не только на основе входных параметров, но и на основе того, кто вызывает метод и т. Д. Они могут быть использованы для обеспечения применения механизмов контроля доступа для защиты определенных конечных точек, например, пользователей без администратора. Надеюсь, вы нашли этот блог пост полезным, спасибо за чтение!
Оригинал: “https://dev.to/rohithpr/python-decorators-simplified-1pj4”