🐍 Самоучитель по Python для начинающих. Часть 14: Функции высшего порядка, замыкания и декораторы
Разберем важные концепции, связанные с функциями высшего порядка, напишем собственные версии map(), reduce() и filter(), потренируемся в создании декораторов и решим 10 практических заданий.
В программировании (и в математике) функциями высшего
порядка называются функции, которые выполняют одно (или оба) из этих действий:
Принимают одну (и более) функций в качестве аргументов.
Возвращают функцию в качестве результата.
Все остальные функции считаются функциями первого порядка. Вот
простейший пример обработки нескольких функций первого порядка multiply(), power(),
add(), subtract()функцией
высшего порядка higher_order():
Вывод:
Декораторы в Python
Синтаксис Python позволяет использовать декораторы для
получения результата «прохождения» функции первого порядка через функцию высшего
порядка. Декоратор – это функция высшего порядка, которая принимает функцию
первого порядка и добавляет в результат что-нибудь от себя, не вмешиваясь в
логику полученной функции:
Вывод:
Более того, Pythonпозволяет писать функции, которые создают декораторы:
Выводдляn = 5:
Поскольку функции в Pythonявляются объектами (класса function), при желании их
можно добавлять в словари или списки:
Вывод:
Порядок перечисления декораторов имеет значение – в
приведенном выше примере в стек попала функция add(), уже измененная функцией высшего
порядка print_with(). При изменении порядка декораторов результат будет просто
12.
Декораторы очень часто используются при разработке приложений
в Pythonфреймворках – они позволяют программисту использовать мощную
функциональность фреймворка, не задумываясь о том, что именно происходит «под
капотом». В приведенном ниже примере декоратор app.route обеспечиваетмаршрутизацию сайта на основе фреймворка Flask:
А в этом примере из функции представления фреймворка Djangoдекоратор
@login_required проверяет, вошел ли посетитель на сайт, и в зависимости от
результата проверки либо показывает ему страницу, заполненную информацией,
созданной этим конкретным пользователем, либо перенаправляет на страницу входа:
Как работают встроенные функции высшего порядка
В предыдущих главах мы уже неоднократно использовали три
самые популярные встроенные функции высшего порядка – map(), reduce() и filter() для обработки наборов данных. В
этом примере встроенная функция map()
берет на себя ряд преобразований элементов строки:
Если бы функции map() не было, пришлось бы заниматься этими преобразованиями
самостоятельно – с помощью спискового включения:
Или с помощью цикла:
Встроенная функция map(), как и любая другая функция высшего порядка, может принимать любую функцию первого порядка и последовательно применять ее ко всем элементам в полученном наборе данных. В приведенном ниже примере встроенная map() принимает пользовательскую функцию divide():
Вывод:
Встроенная функция map() отличается гибкостью – точно такой же результат можно получить, если передать в нее анонимную лямбда-функцию:
Напишем собственную функцию my_map(), которая будет принимать любую другую функцию первого порядка, например, my_function(), которая повторяет полученное число столько раз, чему оно равно:
Вывод:
Как и встроенная map(), пользовательская my_map() может принимать анонимные функции:
Вывод:
Замыкания и вложенные функции
В программировании существует концепция, называемая замыканием
(closure), когда
вложенная функция имеет доступ к локальным переменным функции более высокого
порядка, после того, как внешняя функция уже завершила свою работу:
Сохранение доступа к переменным функции более высокого
порядка возможно благодаря своеобразному виртуальному контейнеру – стеку,
принцип работы которого мы рассматривали в предыдущей статье. В приведенном
ниже примере при вызове outer_function() в стеке сохраняется
фрейм, в котором находятся вложенная функция inner_function()(как константа) и строка text_1 (как локальная переменная). Поскольку функция inner_function()
ссылается на переменную text_1, значение переменной остается доступным после
того, как функция высшего порядка уже завершила свою работу.
Вывод:
На практике замыкания используются для инкапсуляции кода и скрытия важных данных. С помощью замыканий также можно избежать использования глобальных переменных.
Важно заметить, что не всякая вложенная функция автоматически
выступает в качестве замыкания, то есть сохраняет значения всех переменных из
доступной области видимости:если во
вложенной функции нет ссылок на какие-то переменные из функции высшего порядка,
они не будут сохранены, и в этом случае вложенная функция считается просто
вложенной функцией, а не замыканием. В приведенном ниже примере интерпретатор Pythonдемонстрирует,
что именно он посчитал замыканием, а что – нет:
Значение переменной text2 не было сохранено в __closure__, в отличие от использованного во вложенной функции nested() значения text1 – если выполнить
команду my_function.__closure__[1].cell_contents, получим ошибку, так как
никаких других значений в кортеже нет:
В этом можно также убедиться, выполнив команду по выводу имен
переменных, ставших замыканиями:
Практика
Задание 1
Напишите функцию высшего порядка, которая получает в
качестве аргумента две функции первого порядка:
Функцию для преобразования текста сообщения в верхний регистр.
Функцию для преобразования текста сообщения в нижний регистр.
Пример вывода:
Решение:
Задание 2
Напишите функцию высшего порядка, которая может принимать
функции float(), hex(), bin(), str() и содержит вложенную функцию первого порядка, которая
конвертирует полученное от пользователя целое числоnв соответствии
с полученными функциями.
Пример вывода для n = 25:
Решение:
Задание 3
Напишите функцию высшего порядка, которая:
Определяет, состоит ли полученный от пользователя список из четного или нечетного количества чисел.
Возвращает функцию умножения элементов списка (в которой не используется math.prod()), если количество чисел четное.
Возвращает функцию суммирования (в которой не используется встроенная функция sum()), если количество чисел нечетное.
Пример ввода 1:
Вывод 1:
Пример ввода 2:
Вывод 2:
Решение:
Задание 4
Напишите собственный аналог функции filter(). Для отбора данных my_filter() должна, как и встроенная filter(), использовать функцию-предикат. Функция-предикат возвращает Trueили False в зависимости от
критерия – в нашем случае это факт совпадения первой и последней букв слова в
строке, полученной от пользователя.
Пример ввода:
Вывод:
Решение 1:
Решение 2:
Задание 5
Напишите собственный вариант функции-агрегатора reduce(). Функция при вызове
должна получать:
Функцию первого порядка для проведения операции умножения или сложения.
Список чисел от пользователя.
Начальное значение – 0 для операции суммирования, 1 для операции умножения.
Примерввода:
Примеры вызова:
Вывод:
Решение:
Задание 6
В предыдущих статьях мы неоднократно использовали встроенную
функцию zip() для
параллельной итерации двух наборов данных. Напишите функцию высшего порядка,
которая возвращает функции для:
Группировки параллельных элементов списков с помощью самописной my_zip().
Конкатенации элементов, сгрупированных my_zip().
Сложения элементов, сгруппированных my_zip().
Примечание: следует учесть, что получаемые от пользователя
списки могут быть разной длины. Как и встроенная zip(), my_zip() должна ограничивать размер возвращаемого списка длиной
более короткого набора данных.
Пример ввода:
Вывод:
Решение:
Задание 7
Напишите программу, которая:
получает от пользователя список слов и букву на отдельных строках;
с помощью самописной функции my_filter() определяет, какие слова начинаются с полученной буквы;
выводит индексы и слова отфильтрованного списка с помощью самописной функции my_enumerate().
Пример ввода:
Вывод:
Решение:
Задание 8
Напишите функцию высшего порядка, которая принимает две одноаргументные
функции первого порядка и возвращает новую функцию. Эта функция принимает аргумент xи
применяет к нему полученные функции в следующем порядке:
К примеру, если передать в функцию высшего порядка эти функции:
И вызвать функцию так:
Результат будет выглядеть следующим образом:
Решение 1:
Решение 2:
Задание 9
Напишите декоратор, который будет подсчитывать количество
позиционных и именованных аргументов, переданных в функцию первого порядка.
Пример функции и вызова 1:
Вывод 1:
Пример функции и вызова 2:
Вывод 2:
Решение:
Задание 10
Напишите декоратор, который будет измерять производительность
функций, создающих список с помощью этих методов:
range()
списковое включение
append()
конкатенация
Среди показателей должны быть:
Время работы функции.
Текущее потребление памяти.
Пиковое потребление памяти.
Пример вызова:
Вывод:
Решение:
Подведем итоги
Функции высшего порядка помогают производить сложные вычисления, и делают код максимально абстрактным и компактным. Кроме того, с помощью функций высшего порядка реализуют декораторы, которые широко используются при разработке, и позволяют не писать с нуля часть функциональности, необходимой для работы проекта.
В следующей статье будем работать с файлами и файловой системой.
Комментарии