← Назад к вопросам

Что такое каррирование функции?

2.0 Middle🔥 101 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Каррирование функции (Function Currying)

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

Термин происходит от имени математика Хаскелла Карри (Haskell Curry).

Основная идея

Обычная функция:
f(a, b, c) → результат

Каррированная функция:
f(a) → g(b) → h(c) → результат

Простой пример

# Обычная функция
def add(a, b, c):
    return a + b + c

print(add(1, 2, 3))  # 6

# Каррированная функция
def add_curried(a):
    def add_b(b):
        def add_c(c):
            return a + b + c
        return add_c
    return add_b

print(add_curried(1)(2)(3))  # 6

# Или более читаемо
add_1 = add_curried(1)
add_1_2 = add_1(2)
result = add_1_2(3)
print(result)  # 6

Отличие каррирования от частичного применения

from functools import partial

# Обычная функция
def multiply(a, b, c):
    return a * b * c

# Частичное применение (Partial Application)
# Заполняем один параметр, остальные остаются
multiply_by_2 = partial(multiply, 2)
print(multiply_by_2(3, 4))  # 2 * 3 * 4 = 24

multiply_by_2_3 = partial(multiply_by_2, 3)
print(multiply_by_2_3(4))  # 2 * 3 * 4 = 24

# Каррирование
# Преобразуем функцию чтобы она принимала параметры по одному
def multiply_curried(a):
    return lambda b: lambda c: a * b * c

print(multiply_curried(2)(3)(4))  # 24

Преимущества каррирования

1. Переиспользование с частичными данными

def multiply_curried(a):
    return lambda b: lambda c: a * b * c

# Создаём специализированные функции
double = multiply_curried(2)  # multiply by 2
triple = multiply_curried(3)  # multiply by 3

# Используем несколько раз
print(double(5)(10))  # 2 * 5 * 10 = 100
print(triple(4)(2))   # 3 * 4 * 2 = 24

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

def compose(*functions):
    def composed(x):
        for f in reversed(functions):
            x = f(x)
        return x
    return composed

def add_curry(a):
    return lambda x: x + a

def multiply_curry(a):
    return lambda x: x * a

# (x * 2) + 5
f = compose(add_curry(5), multiply_curry(2))
print(f(3))  # (3 * 2) + 5 = 11

3. Создание конфигурируемых функций

def logger_curry(level):
    return lambda service: lambda message: print(f"[{level}][{service}] {message}")

# Создаём логгеры с разными уровнями
error_logger = logger_curry("ERROR")
warn_logger = logger_curry("WARN")
info_logger = logger_curry("INFO")

# Создаём логгеры для разных сервисов
api_error = error_logger("API")
db_error = error_logger("DB")

# Используем
api_error("Connection timeout")    # [ERROR][API] Connection timeout
db_error("Query failed")           # [ERROR][DB] Query failed

Автоматическое каррирование

from functools import wraps

def curry(func):
    """Декоратор для автоматического каррирования"""
    @wraps(func)
    def curried(*args, **kwargs):
        if len(args) + len(kwargs) >= func.__code__.co_argcount:
            return func(*args, **kwargs)
        return lambda *a, **kw: curried(*args, *a, **{**kwargs, **kw})
    return curried

@curry
def add(a, b, c, d):
    return a + b + c + d

# Можем использовать с разным количеством параметров
print(add(1)(2)(3)(4))      # 10
print(add(1, 2)(3)(4))      # 10
print(add(1, 2, 3)(4))      # 10
print(add(1)(2, 3, 4))      # 10

Практические примеры

1. HTTP запросы

def http_request_curry(method):
    return lambda url: lambda headers: lambda data: {
        "method": method,
        "url": url,
        "headers": headers,
        "data": data
    }

# Создаём специализированные функции
get = http_request_curry("GET")
post = http_request_curry("POST")

# Создаём фиксированные URL конфигурации
api_get = get("https://api.example.com")
api_post = post("https://api.example.com")

# Используем с разными заголовками и данными
request1 = api_get({"Authorization": "Bearer token"})(None)
request2 = api_post({"Content-Type": "application/json"})({"user": "john"})

2. Валидация данных

def validator_curry(field_name):
    return lambda field_type: lambda value: {
        "field": field_name,
        "type": field_type,
        "value": value,
        "valid": isinstance(value, field_type)
    }

# Создаём валидаторы
validate_string = validator_curry("name")(str)
validate_number = validator_curry("age")(int)

# Используем
result1 = validate_string("John")      # valid: True
result2 = validate_string(123)         # valid: False
result3 = validate_number(25)          # valid: True
result4 = validate_number("invalid")   # valid: False

3. Цепочка данных (Pipeline)

def pipeline_curry(value):
    return lambda func: lambda: func(value)

# Обработка данных
def double(x): return x * 2
def add_ten(x): return x + 10
def square(x): return x * x

# Создаём цепочку
result = 5
result = double(result)      # 10
result = add_ten(result)     # 20
result = square(result)      # 400

print(result)  # 400

# Или с использованием каррирования (более функциональный подход)
from functools import reduce

def compose_functions(*funcs):
    def composed(x):
        return reduce(lambda result, f: f(result), funcs, x)
    return composed

pipeline = compose_functions(double, add_ten, square)
print(pipeline(5))  # 400

Каррирование в функциональном программировании

# Функциональный подход к обработке списков
def map_curry(func):
    return lambda items: [func(item) for item in items]

def filter_curry(predicate):
    return lambda items: [item for item in items if predicate(item)]

def reduce_curry(func):
    return lambda acc: lambda items: reduce(func, items, acc)

# Используем
numbers = [1, 2, 3, 4, 5]

double_all = map_curry(lambda x: x * 2)
filter_even = filter_curry(lambda x: x % 2 == 0)
sum_all = reduce_curry(lambda acc, x: acc + x)(0)

# Цепочка обработки
result = sum_all(filter_even(double_all(numbers)))
print(result)  # (2 + 4 + 6 + 8 + 10) = 30

Сравнение разных подходов

# Обычная функция
def calculate(operation, a, b):
    if operation == "add": return a + b
    if operation == "multiply": return a * b

result1 = calculate("add", 5, 3)  # Нужно передать операцию каждый раз

# С partial
from functools import partial
add = partial(calculate, "add")
result2 = add(5, 3)

# С каррированием
def calculate_curried(operation):
    return lambda a: lambda b: {
        "add": a + b,
        "multiply": a * b
    }[operation]

add_curried = calculate_curried("add")
result3 = add_curried(5)(3)

Когда использовать каррирование

Хорошо для:

  • Функциональное программирование
  • Создание специализированных функций
  • Композиция функций
  • Частичное применение

Плохо для:

  • Простые задачи
  • Когда нужна читаемость
  • Высокая производительность

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

Что такое каррирование функции? | PrepBro