Приёмы функционального программирования

Что такое замыкание?

подробнее

Замыкание (closure) — это функция, которая запоминает значения из enclosure (охватывающей/внешней) области видимости, даже когда она вызывается вне этой области.

Пример:

def outer_function(x):
    def inner_function(y):
        return x + y  # x берется из внешней области видимости
    return inner_function

# Создаем замыкание
closure = outer_function(10)
result = closure(5)  # 15

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

Фабрика функций:

def make_multiplier(n):
    def multiplier(x):
        return x * n
    return multiplier

double = make_multiplier(2)
triple = make_multiplier(3)

print(double(5))  # 10
print(triple(5))  # 15

Сохранение состояний:

def counter():
    count = 0
    def increment():
        nonlocal count
        count += 1
        return count
    return increment

my_counter = counter()
print(my_counter())  # 1
print(my_counter())  # 2
print(my_counter())  # 3

Особенности

  • Переменные из внешней области сохраняются в __closure__
  • Используют nonlocal для изменения переменных внешней функции
  • Более “легковесная” альтернатива классам для простых случаев

Что такое декоратор?

подробнее

Декоратор - это паттерн проектирования, предназначенный для подключения поведения к объекту. Т.е это некий объект который добавляет поведение другому объекту, без использования наследования.

В Python декоратор реализуется как функция, которая принимает другую функцию в качестве аргумента и возвращает новую функцию, расширяя или изменяя поведение исходной функции без её изменения.

Примеры:

# Базовая структура
from functools import wraps

def my_decorator(func):
    @wraps(func)  # Сохраняет метаданные оригинальной функции
    def wrapper(*args, **kwargs):
        # Код до вызова функции
        result = func(*args, **kwargs)
        # Код после вызова функции
        return result
    return wrapper

@my_decorator
def my_function():
    pass
# Декоратор с параметрами
def repeat(times):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(3)
def say_hello():
    print("Hello!")

say_hello()  # Вывод: Hello! Hello! Hello!
# Класс-декоратор
class CountCalls:
    def __init__(self, func):
        self.func = func
        self.count = 0
    
    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"Функция вызвана {self.count} раз")
        return self.func(*args, **kwargs)

@CountCalls
def say_hello():
    print("Hello!")
# Дкоратор для класса
def class_decorator(cls):
    # Модифицируем класс
    return cls  # или возвращаем новый класс

@class_decorator
class MyClass:
    pass

Что такое рекурсия?

подробнее

Рекурсия — это процесс, при котором функция вызывает саму себя для решения задачи.
Рекурсивная функция должна иметь:

  • Базовый случай — условие остановки рекурсии
  • Рекурсивный случай — вызов функции с измененными параметрами

Пример (вычисление факториала):

# Обычная (итеративная функция)
def factorial_iterative(n):
    result = 1
    for i in range(1, n + 1):
        result *= i
    return result

print(factorial_iterative(5))  # 120
# Рекурсивная функция
def factorial_recursive(n):
    # Базовый случай
    if n == 0 or n == 1:
        return 1
    # Рекурсивный случай
    return n * factorial_recursive(n - 1)

print(factorial_recursive(5))  # 120

Какие ограничения есть у рекурсии?

подробнее

Максимальная глубина рекурсии:

import sys

# Просмотр текущего лимита
print(sys.getrecursionlimit())  # Обычно 1000

# Изменение лимита (осторожно!)
sys.setrecursionlimit(2000)

Функции map, filter, reduce

подробнее

map()

map() применяет функцию к каждому элементу итерируемого объекта и возвращает итератор с результатами.

# синтаксис
map(function, iterable)

# пример (возведение в квадрат)
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, numbers))
print(squared)  # [1, 4, 9, 16, 25]

filter()

filter() фильтрует элементы итерируемого объекта, оставляя только те, для которых функция возвращает True.

# синтаксис
filter(function, iterable)

# пример (фильтрация четных чисел)
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
print(even_numbers)  # [2, 4, 6, 8, 10]

reduce()

reduce() применяет функцию кумулятивно к элементам итерируемого объекта, сводя его к единственному значению.

# синтаксис
from functools import reduce
reduce(function, iterable[, initializer])

# пример (сумма всех элементов)
numbers = [1, 2, 3, 4, 5]
total = reduce(lambda x, y: x + y, numbers)
print(total)  # 15