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

⚡ Кратко

Функция высшего порядка — функция, которая принимает другую функцию как аргумент или возвращает функцию.

filter(fn, iterable) оставляет только элементы, для которых fn вернёт истину.

reduce(fn, iterable) сворачивает последовательность в одно значение.

sorted(iterable, key=fn) сортирует, применяя fn к каждому элементу для вычисления ключа.

lambda x: ... — короткая анонимная функция.

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

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

def apply_twice(func: callable, value: int) -> int:
    """Применяет func к value дважды."""
    return func(func(value))


def add_five(x: int) -> int:
    return x + 5


print(apply_twice(add_five, 3))  # 13

Здесь apply_twice — функция высшего порядка, потому что её первый аргумент тоже функция.

Фильтрация: filter и предикаты

Предикат — функция, которая возвращает bool. Её можно передать в filter, чтобы оставить только нужные элементы.

def is_even(n: int) -> bool:
    return n % 2 == 0


nums = [1, 2, 3, 4, 5, 6]
evens = list(filter(is_even, nums))
print(evens)  # [2, 4, 6]

Функция filter возвращает итератор, поэтому результат обычно оборачивают в list() или обходят в цикле.

Лямбда-функции

lambda позволяет создать маленькую анонимную функцию прямо в месте использования. Она подходит, когда логика настолько простая, что отдельное def усложнит код.

nums = [1, 2, 3, 4, 5, 6]
evens = list(filter(lambda x: x % 2 == 0, nums))
print(evens)  # [2, 4, 6]
💡 На заметку: если логика перестаёт помещаться в одну строку, лучше вернуться к обычной функции def. Читаемость важнее краткости.

Агрегирование: reduce

Функция reduce из модуля functools последовательно применяет функцию к парам элементов и сворачивает коллекцию в одно значение.

from functools import reduce

numbers = [1, 2, 3, 4, 5]
product = reduce(lambda acc, x: acc * x, numbers, 1)
print(product)  # 120

Третий аргумент 1 — начальное значение аккумулятора. Оно важно для пустых коллекций: без него reduce выбросит исключение.

Сортировка по ключу

Встроенная функция sorted принимает параметр key — функцию, которая вычисляет значение для сравнения. Это позволяет сортировать строки по длине, числа по абсолютной величине и так далее.

words = ["apple", "banana", "kiwi", "grape"]
sorted_words = sorted(words, key=len)
print(sorted_words)  # ['kiwi', 'grape', 'apple', 'banana']

Функция len вызывается для каждого слова, и Python сортирует слова по полученным числам, а не по алфавиту.

Аннотации типов

Источник требует добавлять аннотации к решённым функциям. Аннотации не влияют на выполнение, но помогают IDE и статическим анализаторам находить ошибки.

from typing import Callable


def filter_numbers(
    predicate: Callable[[int], bool],
    numbers: list[int]
) -> list[int]:
    return [n for n in numbers if predicate(n)]

Запись Callable[[int], bool] означает: функция, которая принимает один int и возвращает bool.

⚠️ Проверить по документации: в Python 3.9+ встроенные коллекции можно использовать как generic-типы: list[int], dict[str, int]. В более старых версиях для этого нужен модуль typing с заглавными именами: List[int], Dict[str, int].

Композиция функций

Несколько функций можно применять последовательно: результат одной передаётся в следующую. Этот приём используется в задании про цепочку шифрования.

def compose(value, *functions: callable):
    for func in functions:
        value = func(value)
    return value

Такой подход делает цепочку преобразований явной и легко расширяемой.