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

Что такое функтор (Functor)?

2.0 Middle🔥 111 комментариев
#DevOps и инфраструктура#Django

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

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

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

Функтор (Functor)

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

Базовое определение

class Multiplier:
    """Функтор для умножения на число"""
    
    def __init__(self, factor: int):
        self.factor = factor
    
    def __call__(self, x: int) -> int:
        """Это делает объект вызываемым (callable)"""
        return x * self.factor

# Используем функтор
multiply_by_2 = Multiplier(2)
multiply_by_5 = Multiplier(5)

print(multiply_by_2(10))  # 20
print(multiply_by_5(10))  # 50

# Функтор запоминает состояние
print(multiply_by_2.factor)  # 2

Сравнение: Функция vs Функтор

# Функция (без состояния)
def create_multiplier(factor):
    return lambda x: x * factor

multiply_by_2_func = create_multiplier(2)
print(multiply_by_2_func(10))  # 20

# Функтор (с состоянием)
class Multiplier:
    def __init__(self, factor):
        self.factor = factor
    
    def __call__(self, x):
        return x * self.factor

multiply_by_2_functor = Multiplier(2)
print(multiply_by_2_functor(10))  # 20

# Разница: функтор может хранить дополнительное состояние
class Counter:
    def __init__(self, multiplier=1):
        self.multiplier = multiplier
        self.call_count = 0
    
    def __call__(self, x):
        self.call_count += 1
        print(f"Вызов #{self.call_count}")
        return x * self.multiplier

counter = Counter(2)
print(counter(5))  # Вызов #1 → 10
print(counter(5))  # Вызов #2 → 10
print(counter.call_count)  # 2

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

Пример 1: Функтор для логирования

import time
from typing import Callable, Any

class Logger:
    """Функтор для логирования вызовов функций"""
    
    def __init__(self, func: Callable, name: str = None):
        self.func = func
        self.name = name or func.__name__
        self.call_count = 0
    
    def __call__(self, *args, **kwargs) -> Any:
        self.call_count += 1
        print(f"[{self.call_count}] Вызов {self.name} с аргументами {args}, {kwargs}")
        
        start_time = time.time()
        result = self.func(*args, **kwargs)
        elapsed = time.time() - start_time
        
        print(f"[{self.call_count}] Результат: {result} (за {elapsed:.4f}s)")
        return result

def add(a, b):
    time.sleep(0.1)
    return a + b

# Обёртываем функцию в функтор
logged_add = Logger(add, "add")
print(logged_add(2, 3))  # [1] Вызов add..., [1] Результат: 5
print(logged_add(5, 7))  # [2] Вызов add..., [2] Результат: 12

Пример 2: Функтор для кэширования

from typing import Dict, Tuple, Any

class Memoizer:
    """Функтор для кэширования результатов функции"""
    
    def __init__(self, func: Callable):
        self.func = func
        self.cache: Dict[Tuple, Any] = {}
    
    def __call__(self, *args, **kwargs) -> Any:
        # Создаём ключ кэша из аргументов
        key = (args, tuple(sorted(kwargs.items())))
        
        if key in self.cache:
            print(f"Результат из кэша: {args}")
            return self.cache[key]
        
        print(f"Вычисляем: {args}")
        result = self.func(*args, **kwargs)
        self.cache[key] = result
        return result

def fibonacci(n):
    """Вычисление чисел Фибоначчи"""
    if n <= 1:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

fib = Memoizer(fibonacci)
print(fib(5))  # Вычисляем: (5,) → 5
print(fib(5))  # Результат из кэша: (5,) → 5
print(len(fib.cache))  # Размер кэша

Пример 3: Функтор для обработки событий

class EventHandler:
    """Функтор для обработки событий"""
    
    def __init__(self, event_type: str):
        self.event_type = event_type
        self.listeners = []
    
    def __call__(self, data: Any) -> None:
        """Вызываем все слушателей"""
        print(f"Событие '{self.event_type}' с данными: {data}")
        for listener in self.listeners:
            listener(data)
    
    def subscribe(self, listener: Callable) -> None:
        """Добавить слушателя"""
        self.listeners.append(listener)

# Создаём обработчик события
click_handler = EventHandler("click")

# Подписываем слушателей
click_handler.subscribe(lambda data: print(f"  → Логируем клик: {data}"))
click_handler.subscribe(lambda data: print(f"  → Отправляем аналитику: {data}"))

# Вызываем событие
click_handler({"x": 100, "y": 200})

Функторы в функциональном программировании

from typing import Callable, TypeVar, Generic

T = TypeVar('T')
U = TypeVar('U')

class Compose:
    """Функтор для композиции функций"""
    
    def __init__(self, *funcs: Callable):
        self.funcs = funcs
    
    def __call__(self, x):
        """Применяем функции справа налево (как в математике)"""
        for func in reversed(self.funcs):
            x = func(x)
        return x

# Использование
def double(x):
    return x * 2

def add_one(x):
    return x + 1

def square(x):
    return x ** 2

# Композиция: (x + 1) * 2 в квадрате
composed = Compose(square, double, add_one)
print(composed(3))  # ((3 + 1) * 2) ** 2 = (4 * 2) ** 2 = 64

Встроенные функторы в Python

# map и filter уже принимают вызываемые объекты
class IsEven:
    """Функтор для проверки чётности"""
    def __call__(self, x):
        return x % 2 == 0

class Double:
    """Функтор для удвоения"""
    def __call__(self, x):
        return x * 2

numbers = [1, 2, 3, 4, 5, 6]

filterer = IsEven()
print(list(filter(filterer, numbers)))  # [2, 4, 6]

doubler = Double()
print(list(map(doubler, numbers)))  # [2, 4, 6, 8, 10, 12]

# sorted тоже принимает функтор как key
class ReverseOrder:
    def __call__(self, x):
        return -x

print(sorted(numbers, key=ReverseOrder()))  # [6, 5, 4, 3, 2, 1]

Функтор как декоратор

class Decorator:
    """Функтор-декоратор"""
    
    def __init__(self, func: Callable):
        self.func = func
        self.call_count = 0
    
    def __call__(self, *args, **kwargs):
        self.call_count += 1
        print(f"Вызов #{self.call_count}")
        return self.func(*args, **kwargs)

@Decorator
def greet(name):
    return f"Привет, {name}!"

print(greet("Alice"))  # Вызов #1, Привет, Alice!
print(greet("Bob"))    # Вызов #2, Привет, Bob!
print(greet.call_count)  # 2

Функтор с параметрами (partial)

from functools import partial

def multiply(x, y):
    return x * y

# partial создаёт функтор с предзаполненными аргументами
multiply_by_2 = partial(multiply, 2)
multiply_by_3 = partial(multiply, 3)

print(multiply_by_2(10))  # 20
print(multiply_by_3(10))  # 30

# Свой функтор для того же
class Partial:
    def __init__(self, func, *args, **kwargs):
        self.func = func
        self.args = args
        self.kwargs = kwargs
    
    def __call__(self, *args, **kwargs):
        all_args = self.args + args
        all_kwargs = {**self.kwargs, **kwargs}
        return self.func(*all_args, **all_kwargs)

custom_multiply = Partial(multiply, 2)
print(custom_multiply(10))  # 20

Сравнение: Функтор vs Лямбда vs Замыкание

# 1. Функтор (сохраняет состояние в объекте)
class Accumulator:
    def __init__(self, start=0):
        self.total = start
    
    def __call__(self, x):
        self.total += x
        return self.total

acc1 = Accumulator(0)
print(acc1(5))  # 5
print(acc1(3))  # 8

# 2. Замыкание (функция запоминает переменную)
def make_accumulator(start=0):
    total = start
    def add(x):
        nonlocal total
        total += x
        return total
    return add

acc2 = make_accumulator(0)
print(acc2(5))  # 5
print(acc2(3))  # 8

# 3. Лямбда (одноразовые функции)
print((lambda x: x * 2)(5))  # 10

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

  1. Когда нужно сохранить состояние — объект запоминает данные
  2. Когда нужна переиспользуемая логика — создаём несколько экземпляров
  3. Для стратегий и обработчиков — логирование, кэширование, валидация
  4. Для расширяемости — легче добавлять методы
  5. Для читаемости — явный интерфейс через класс

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

Что такое функтор (Functor)? | PrepBro