Функции высшего порядка
В Python функции являются объектами, поэтому их можно присваивать переменным, возвращать из других функций и передавать в качестве аргументов другим функциям.
И если функция делает хотя бы одно из двух:
- принимает одну или несколько других функций в качестве аргументов;
- возвращает функцию как результат своей работы.
То такая функция называется функцией высшего порядка.
Одной из таких функций является функция sorted(), с которой мы познакомились, когда говорили о сортировке списков. Ей можно передать функцию, которая будет применяться к каждому элементу итерируемого объекта перед сортировкой:
vegetables = ["Баклажан", "Щавель", "Тыква", "Сельдерей", "Спаржа"]
print(sorted(vegetables, key=len)) # Сортировка по длине строки
# Вывод: ['Тыква', 'Щавель', 'Спаржа', 'Баклажан', 'Сельдерей']
Поэтому функция sorted() является функцией высшего порядка. Кроме неё в Python представлены функции map() и filter().
Функция map()
Функция map() применяет заданную функцию к каждому элементу итерируемого объекта (например, списка, кортежа) и возвращает итератор с результатами.
|
Функция |
|
|
Описание |
Возвращает итератор из элементов, полученных в результате применения функции |
|
Параметры |
|
|
Возвращаемое значение |
Итератор |
Допустим, у нас есть список чисел, и мы хотим получить новый список, где каждое число возведено в квадрат, а затем из него вычтена единица. Для этого сначала напишем функцию custom_square(), которая совершает эту операцию над переданным ей числом:
def custom_square(number: int) -> int:
return number ** 2 - 1
Затем применим её к каждому элементу списка numbers с помощью функции map():
numbers = [1, 2, 3, 4, 5]
squared_numbers = map(custom_square, numbers)
print(list(squared_numbers))
# Вывод: [0, 3, 8, 15, 24]
Здесь функция map() сначала применяет функцию custom_square() к первому элементу списка и получает 0, затем применяет эту же функцию ко второму элементу списка и получает 3, и так далее. Она не изменяет исходную коллекцию, а возвращает итератор, который можно преобразовать в одну из коллекций, например, список.
Часто функция map() находит применение в обработке данных, введённых пользователем. Так, функция input() всегда возвращает строку, поэтому при разбиении её на список методом str.split() каждый элемент этого списка тоже будет строкой. Для того, чтобы работать с каждым элементом как с числом, к нему следует применить функцию int():
numbers = map(int, input().split())
print(sum(numbers))
# Ввод: 1 2 3
# Вывод: 6
Здесь функция map() применяет к каждому элементу списка, полученного из строки, функцию int() и возвращает итератор, который мы можем передать любой функции, работающей с итерируемыми объектами, например, sum() для вычисления суммы элементов.
Также функции map() можно передать не один, а несколько итерируемых объектов. Тогда она будет брать по одному элементу из каждого, поэтому функция должна принимать столько же аргументов, сколько итерируемых объектов было передано в map().
Допустим, мы хотим поэлементно сложить три списка. Вместо того, чтобы перебирать эти списки в цикле, напишем функцию custom_plus(), которая складывает три переданные ей числа:
def custom_plus(number1: int, number2: int, number3: int) -> int:
return number1 + number2 + number3
И применим её к трём спискам:
numbers1 = [1, 2, 3, 4, 5]
numbers2 = [2, 3, 4, 5, 6]
numbers3 = [3, 4, 5, 6, 7, 8]
sum_numbers = map(custom_plus, numbers1, numbers2, numbers3)
print(list(sum_numbers))
# Вывод: [6, 9, 12, 15, 18]
Здесь функция map() берёт по одному элементу из каждого переданного списка и применяет к ним функцию custom_plus(), то есть складывает их. При этом функция map() останавливается, когда исчерпывается самая короткая коллекция, поэтому последний элемент списка numbers3 не был обработан.
Функция filter()
Функция filter() создает итератор, который содержит только те элементы из итерируемого объекта, для которых заданная функция возвращает истинное значение True.
|
Функция |
|
|
Описание |
Возвращает итератор только из тех элементов итерируемого объекта iterable, для которых функция function вернула True |
|
Параметры |
|
|
Возвращаемое значение |
Итератор |
Функция filter(), как и map(), принимает функцию и итерируемый объект. Однако она не преобразует элементы, а отбирает только те из них, для которых функция возвращает True.
Допустим, у нас есть список чисел, и мы хотим отфильтровать его, чтобы остались только чётные. Для этого нам нужно написать функцию is_even(), которая, если число чётное, возвращает True, а иначе – False:
def is_even(number: int) -> bool:
return number % 2 == 0
Затем мы можем применить её к каждому элементу списка numbers, и в результате получим итератор только из тех элементов, для которых функция вернула True:
numbers = [1, 2, 3, 4, 5, 6, 7, 8]
even_numbers = filter(is_even, numbers)
print(list(even_numbers))
# Вывод: [2, 4, 6, 8]
Здесь функция filter() проверяет каждое число с помощью функции is_even(). Для числа 1 она возвращает False, поэтому оно пропускается в итоговой последовательности, а для числа 2 она возвращает True, поэтому оно включается в результат, и так далее.
Примеры
Пример 1. Конвертер цен
Цены на товары в международном интернет-магазине представлены в рублях и хранятся в списке. Функция map() применяет функцию конвертации convert_to_usd() к каждому элементу списка цен:
def convert_to_usd(price_in_rub: int | float) -> float:
"""Конвертирует цену из рублей в доллары.
Параметры:
price_in_rub: Цена товара в рублях.
Возвращает:
Цена товара в долларах.
"""
exchange_rate = 80.7 # Условный курс
return round(price_in_rub / exchange_rate, 2)
rubles = [1250, 2500, 5000, 750]
# Конвертируем все цены с помощью map()
usd_prices = map(convert_to_usd, rubles)
print("Цены в рублях:", *rubles)
print("Цены в долларах:", *usd_prices)
Оператор * распаковывает элементы списка rubles и итератора usd_prices, передавая их в функцию print() как отдельные аргументы.
Вывод:
Цены в рублях: 1250 2500 5000 750
Цены в долларах: 15.49 30.98 61.96 9.29
Пример 2. Фильтрация списка транзакций
Список транзакций содержит положительные и отрицательные числа. Функция filter() позволяет получить итератор, содержащий только те транзакции, которые являются расходами (отрицательные числа). Для этого используется функция is_expense(), которая проверяет, является ли число отрицательным:
def is_expense(amount: float) -> bool:
"""Проверяет, является ли транзакция расходом.
Параметры:
amount: Сумма транзакции.
Возвращает:
True (расход) или False (доход).
"""
return amount < 0
transactions = [1000, -2500, 5200, -120, -1500, 800]
# Фильтруем транзакции, оставляя только расходы
expenses = filter(is_expense, transactions)
print("Все транзакции:", *transactions)
print("Только расходы:", *expenses)
Вывод:
Все транзакции: 1000 -2500 5200 -120 -1500 800
Только расходы: -2500 -120 -1500
Итоги
- Функция высшего порядка – это функция, которая принимает функции в качестве аргументов и/или возвращает функцию.
- Функция
map()применяет переданную функцию к каждому элементу итерируемого объекта и возвращает итератор с результатами. - Функция
filter()применяет переданную функцию к каждому элементу итерируемого объекта и возвращает итератор только с теми элементами, для которых функция вернулаTrue.
Задания для самопроверки
1. Что такое функции высшего порядка? Приведите примеры.
Это функции, которые принимает одну или несколько других функций в ка-честве аргументов и/или возвращает другую функцию в качестве своего ре-зультата. К ним относятся встроенные функции sorted(), map() и filter().
2. Напишите функцию reduce_by_ten_percent(x), которая принимает число x и возвращает его, уменьшив на 10 %. Используйте функции map() и reduce_by_ten_percent() для того, чтобы уменьшить на 10 % каждое значение списка [105, 102, 24, 46, 50]. Выведите результат на экран.
def reduce_by_ten_percent(x: int | float) -> float:
return x * 0.9
result = list(map(reduce_by_ten_percent, [105, 102, 24, 46, 50]))
print(result)
# Вывод: [94.5, 91.8, 21.6, 41.4, 45.0]
3. Что будет выведено на экран в результате выполнения следующего кода? Объясните свой ответ.
def is_odd(number):
return number % 2 != 0
numbers = [1, 2, 3, 4, 5, 6, 7]
result = filter(is_odd, numbers)
print(list(result))
Будет выведен список [1, 3, 5, 7], так как функция filter() отбирает из списка numbers все нечетные числа.
4. Ознакомьтесь с данным кодом и допишите функцию normalize() таким образом, чтобы она преобразовала фамилии в списке old_names к написанию, при котором первая буква заглавная, а остальные – строчные.
def normalize(name):
pass
old_names = ["савельич", "ГРИНЁВ", "ШВАБРин"]
new_names = list(map(normalize, old_names))
print(new_names)
# Вывод: ['Савельич', 'Гринёв', 'Швабрин']
def normalize(name: str) -> str:
return name.capitalize()
old_names = ["савельич", "ГРИНЁВ", "ШВАБРин"]
new_names = list(map(normalize, old_names))
print(new_names)
# Вывод: ['Савельич', 'Гринёв', 'Швабрин']
5. Напишите функцию is_short_word(text), которая принимает строку text и возвращает True, если её длина меньше или равна 4 символам, и False в противном случае. Используйте эту функцию в filter() и создайте из списка ["Киви", "Пляж", "Физика", "Кот", "Программирование"] новый список, содержащий только короткие слова.
def is_short_word(word: str) -> bool:
return len(word) <= 4
words = ["Киви", "Пляж", "Физика", "Кот", "Программирование"]
short_words_list = list(filter(is_short_word, words))
print(short_words_list)
# Вывод: ['Киви', 'Пляж', 'Кот']
0 комментариев