Какие элементы функционального программирования поддерживает Python?

Ответ

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

Основные элементы:

  • Функции как объекты первого класса: Функции можно присваивать переменным, передавать в качестве аргументов и возвращать из других функций.

  • Функции высшего порядка: Функции, которые принимают другие функции в качестве аргументов или возвращают их. Классические примеры: map(), filter(), reduce().

    numbers = [1, 2, 3, 4]
    # map применяет лямбда-функцию к каждому элементу
    squared = list(map(lambda x: x**2, numbers)) # [1, 4, 9, 16]
  • Лямбда-функции: Анонимные однострочные функции, создаваемые с помощью ключевого слова lambda.

    add = lambda a, b: a + b
    print(add(5, 3)) # 8
  • Замыкания (Closures): Внутренние функции, которые «помнят» и имеют доступ к переменным из локальной области видимости внешней функции, даже после того как внешняя функция завершила свое выполнение.

    def outer_func(msg):
        def inner_func():
            print(msg)
        return inner_func
    
    printer = outer_func("Hello, World!")
    printer() # Выведет "Hello, World!"
  • Иммутабельные типы данных: Встроенные типы, такие как tuple, frozenset, str, чьи значения нельзя изменить после создания. Это ключевая концепция ФП для избежания побочных эффектов.

  • Генераторы и ленивые вычисления: Генераторы, создаваемые с помощью yield, вычисляют значения по мере необходимости, что экономит память и повышает производительность.

  • Модуль functools: Содержит утилиты для работы с функциями, такие как partial для частичного применения функций, reduce для свертки последовательностей и lru_cache для мемоизации.

Ответ 18+ 🔞

Слушай, а вот Python, он вроде как не монах в храме функционального программирования, но, блядь, тащит туда кучу фишек, как будто на халяву в бар приперся. Можно писать почти как на Haskell, только без этих, блядь, мозговыносящих монад на каждом шагу.

Чем он там прикалывается:

  • Функции — это как печеньки в кармане. Их можно сувать куда угодно: в переменные, в аргументы, возвращать из других функций. Полный беспредел, но удобно, ёпта.

    def кричать(текст):
        return текст.upper() + "!!!"
    
    гребаный_мегафон = кричать  # Смотри-ка, функция в переменной!
    print(гребаний_мегафон("тише"))  # ТИШЕ!!!
  • Функции высшего порядка — это такие шеф-повара, которые готовят, используя других поваров (другие функции). Классика жанра — map, filter, reduce. reduce, правда, спрятали, как краденый велосипед, в модуль functools.

    числа = [1, 2, 3, 4]
    # map — как конвейер: берет функцию и применяет ко всему подряд
    в_квадрате = list(map(lambda x: x**2, числа)) # [1, 4, 9, 16]
    # filter — как сито: оставляет только то, что проходет проверку
    чётные = list(filter(lambda x: x % 2 == 0, числа)) # [2, 4]
  • Лямбда-функции — это одноразовые зажигалки. Создал, чиркнул, выкинул. Анонимные и в одну строчку. Для сложной логики — иди нахуй, пиши нормальную функцию.

    прибавить = lambda a, b: a + b
    print(прибавить(5, 3)) # 8
    # Чаще их используют прямо в месте, где нужны, как вот выше в map.
  • Замыкания — это функции с памятью, как слон. Внутренняя функция помнит всё, что было во внешней, даже когда та уже давно сдохла. Прямо мистика какая-то, блядь.

    def создать_приветилку(имя):
        # Внутренняя функция запекает в себе 'имя'
        def приветилка():
            return f"Ну здарова, {имя}, ёпта!"
        return приветилка
    
    позвать_васька = создать_приветилку("Васьок")
    print(позвать_васька()) # Ну здарова, Васьок, ёпта!
    # 'Васьок' уже давно в ином измерении, а функция его помнит. Жуть.
  • Неизменяемые типы — священные коровы. Кортежи (tuple), строки (str), замороженные множества (frozenset). Создал — и хоть тресни, не изменишь. Это чтобы твой код не превращался в свалку непредсказуемых побочных эффектов, где всё плывёт и меняется, как сознание алкаша.

  • Генераторы — это ленивые жопы. Они не делают всю работу сразу, а выдают результат по одному, когда их пнёшь. Экономия памяти — овердохуища, особенно когда данных много.

    def бесконечная_лента_чисел(старт):
        i = старт
        while True:
            yield i  # yield — это волшебное слово "на, держи один, остальные потом"
            i += 1
    
    ленивец = бесконечная_лента_чисел(10)
    print(next(ленивец)) # 10
    print(next(ленивец)) # 11
    # И так до скончания времён, не жрёт память.
  • Модуль functools — это ящик с продвинутым инструментом. Тут тебе и partial (чтобы зафиксировать функции часть аргументов заранее, как приклеить), и reduce (для сворачивания последовательностей в одно значение), и lru_cache (чтобы функция запоминала результаты своих вызовов и не пересчитывала одно и то же по сто раз — умно, блядь).

Короче, Python даёт тебе в руки функциональные фишки, но не заставляет ими пользоваться. Хочешь — пиши в чистом стиле, хочешь — мешай с императивным кодом. Главное — не устраивай пиздец в проекте.