Рубрики
Без рубрики

Упрощенные декораторы Python

Этот блог пост направлен на демистифицировать демоторы Python для начинающих с глупыми, составляющими требования …. Теги с Python.

Этот блог пост направлен на демистифицировать декораторы Python для начинающих с глупыми требованиями.

Концепция: функции высшего порядка

Википедия описывает Функции высшего порядка следующее:

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

  1. принимает один или несколько функций в качестве аргументов
  2. Возвращает функцию в качестве результата.

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

# 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

Тестовые случаи оставались без изменений, и все они проходят. Таким образом, мы знаем, что наш код делает то же самое, что он сделал раньше. Несколько изменений, которые были сделаны:

  1. Определить custom_math_guard До того, как он используется: интерпретатор Python проходит через нашу строку скрипта по линии сверху вниз, и она бросает ошибку, если она находит декоратор, ссылающийся на функцию, пока она еще не видела. Таким образом, важно заказывать методы в правильной последовательности. Это обычно не проблема, поскольку мы, вероятно, импортируйте декораторы из другого файла/пакета. Поскольку импорт выполняется в верхней части файла, мы можем свободно использовать их где угодно в остальной части файла, не беспокоясь о правильном порядке определений функций.
  2. Мы больше не должны определять функцию Добавить а затем назначить результат custom_math_guard (Добавить) к custom_add Отказ Нам не нужно беспокоиться о том, что кто-то непреднамеренно звонит Добавить вместо custom_add или! У нас есть только одна функция, которая делает то, что мы хотим, чтобы это сделать.

Закрытие заметок

Декораторы мощные и могут радикально изменять поток выполнения не только на основе входных параметров, но и на основе того, кто вызывает метод и т. Д. Они могут быть использованы для обеспечения применения механизмов контроля доступа для защиты определенных конечных точек, например, пользователей без администратора. Надеюсь, вы нашли этот блог пост полезным, спасибо за чтение!

Оригинал: “https://dev.to/rohithpr/python-decorators-simplified-1pj4”