← Назад к вопросам
Что такое функтор (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
Когда использовать функторы
- Когда нужно сохранить состояние — объект запоминает данные
- Когда нужна переиспользуемая логика — создаём несколько экземпляров
- Для стратегий и обработчиков — логирование, кэширование, валидация
- Для расширяемости — легче добавлять методы
- Для читаемости — явный интерфейс через класс
Функтор — это мощный паттерн для создания переиспользуемой логики, которая хранит состояние и ведёт себя подобно функции. Он часто используется в Python для декораторов, обработчиков событий и функционального программирования.