Что такое модуль `functools` в Python и каковы его основные функции?

Ответ

Модуль functools — это часть стандартной библиотеки Python, предоставляющая высокоуровневые функции для работы с другими функциями или вызываемыми объектами. Он используется для функционального программирования, оптимизации и упрощения кода.

Основные функции и их применение:

  • @lru_cache(maxsize=128): Декоратор для мемоизации (кэширования) результатов функции. Ускоряет повторные вызовы функций с одинаковыми аргументами, особенно полезен для рекурсивных функций или дорогих вычислений.
  • functools.partial(func, *args, **kwargs): Создаёт новую функцию с частично применёнными аргументами. Позволяет "зафиксировать" некоторые аргументы исходной функции, упрощая её использование в контекстах, где требуется функция с меньшим числом параметров (например, в колбэках).
  • @functools.wraps(wrapped): Декоратор, используемый при создании других декораторов. Он копирует имя, docstring, модуль и другие метаданные оборачиваемой функции в функцию-обёртку, что улучшает отладку и интроспекцию.
  • functools.reduce(function, iterable[, initializer]): Применяет функцию к элементам последовательности, сводя её к единственному значению. (В Python 3 перенесён из встроенных функций в functools).

Пример использования @lru_cache:

from functools import lru_cache

@lru_cache(maxsize=None) # maxsize=None означает неограниченный кэш
def fibonacci(n: int) -> int:
    if n < 2:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

# Без кэша вычисление fibonacci(30) было бы значительно медленнее
print(fibonacci(30))

Пример использования functools.partial:

from functools import partial

def power(base: int, exponent: int) -> int:
    return base ** exponent

# Создаём новую функцию, которая возводит число в квадрат
square = partial(power, exponent=2)
print(square(5)) # Выведет 25 (5^2)

# Создаём функцию, которая возводит 2 в степень
power_of_two = partial(power, base=2)
print(power_of_two(3)) # Выведет 8 (2^3)

Ответ 18+ 🔞

А, functools, говоришь? Ну это ж, блядь, такая штука в Питоне, которая как будто бы взяла все эти ваши умные книжки по функциональщине, переварила их и выплюнула в виде готовых инструментов, чтобы ты не изобретал велосипед с квадратными колёсами. Короче, библиотека для ленивых гениев, чтобы код был короче, а мозг — целее.

Чем там можно разжиться, если покопаться:

  • @lru_cache(maxsize=128): Это, сука, волшебный пендель для твоих тормозных функций. Вешаешь этот декоратор на какую-нибудь рекурсивную поеботу вроде вычисления факториала или чисел Фибоначчи, и она перестаёт считать одно и то же по сто раз. Она просто запоминает, что вот для аргумента 5 ответ был 120, и в следующий раз не лезет в дебри, а сразу плюёт тебе результат. Максимально полезная хуйня, ебать мои старые костыли.
  • *`functools.partial(func, args, kwargs)`: Представь, у тебя есть функция с кучей параметров, а тебе надо её впихнуть куда-то, где ждут функцию с одним аргументом. Вот partial — это как зажим для патронов. Ты говоришь: «Вот эта функция, но давай второй аргумент у неё всегда будет равен двойке». И получаешь новую, урезанную функцию, которая уже готова к бою. Удобно, как хуй с пальто.
  • @functools.wraps(wrapped): Это для параноиков и педантов. Когда ты пишешь свой собственный декоратор и оборачиваешь им другую функцию, то обёрнутая функция теряет своё имя и описание. Весь мир начинает думать, что её зовут wrapper, а не super_calc. Так вот wraps возвращает украденные документы и имя на место, чтобы при отладке не охуевать, глядя в стектрейс.
  • functools.reduce(function, iterable[, initializer]): Старый добрый агрегатор. Берет последовательность и склеивает её в одно значение, применяя функцию попарно. Типа «вот тебе список чисел, а ты просуммируй их». В Питоне 3 её, хитрая жопа, вынесли из встроенных функций сюда, в functools, чтобы народ меньше путался.

Вот смотри, как @lru_cache делает из черепахи гепарда:

from functools import lru_cache

@lru_cache(maxsize=None) # maxsize=None — это типа «кэшируй всё, нахуй»
def fibonacci(n: int) -> int:
    if n < 2:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

# Без этого декоратора вычисление fibonacci(40) заставило бы тебя сходить за кофе, покурить и вернуться.
# А так — чирик, и готово.
print(fibonacci(30))

А вот partial в деле, чтобы не писать одно и то же:

from functools import partial

def power(base: int, exponent: int) -> int:
    return base ** exponent

# Делаем из пушки по воробьям — специализированный дробовик.
# «Возвести в квадрат» — это же просто «power, но exponent=2».
square = partial(power, exponent=2)
print(square(5)) # Напечатает 25 (5 в квадрате, ёпта)

# А это — «power, но base=2». Получаем функцию «двойка в степень».
power_of_two = partial(power, base=2)
print(power_of_two(3)) # Напечатает 8 (2 в кубе, если кто не в курсе)

Вот и весь сказ. Библиотека не огромная, но каждая функция в ней — как отвёртка в правильном наборе инструментов: вроде простая, но когда надо — другой хуйней не заменишь.