Функции в Python — это мощный инструмент для создания читаемого и эффективного кода. Но помимо основ, существует ряд более продвинутых техник, которые позволяют сделать код более гибким и выразительным. В этой статье мы углубимся в несколько важных аспектов:
- Замыкания — когда функция "помнит" свои внешние переменные.
- Декораторы — для расширения функциональности функций.
- Генераторы — для работы с большими данными.
Для этого примера возьмём задачу, которая встречается в реальной разработке: обработка логов с динамической фильтрацией. Мы будем использовать замыкания, декораторы и генераторы, чтобы сделать систему фильтрации и обработки данных гибкой и удобной.

Задача: обработка логов с фильтрацией
Представьте, что у нас есть система для анализа логов. Логи содержат информацию о действиях пользователей в системе:
- user — имя пользователя
- action — действие
- timestamp — временная метка
- status — статус выполнения действия (например, success, failure)
Наша задача — создать систему, которая:
- Фильтрует логи по различным критериям (например, по пользователю или статусу).
- Позволяет динамически изменять фильтры.
- Легко расширяется для добавления новых фильтров.
Для этого примера мы будем использовать три продвинутые техники:
- Замыкания, чтобы динамически запоминать параметры фильтрации.
- Декораторы, чтобы добавлять функциональность для фильтрации.
- Генераторы, чтобы обрабатывать логи эффективно, не загружая память.
Стартовые данные
Вот как могут выглядеть логи:
logs = [
{"user": "alice", "action": "login", "timestamp": "2025-12-01 10:00:00", "status": "success"},
{"user": "bob", "action": "login", "timestamp": "2025-12-01 10:05:00", "status": "failure"},
{"user": "alice", "action": "logout", "timestamp": "2025-12-01 10:10:00", "status": "success"},
{"user": "charlie", "action": "login", "timestamp": "2025-12-01 10:15:00", "status": "success"},
{"user": "bob", "action": "logout", "timestamp": "2025-12-01 10:20:00", "status": "failure"},
]
Наша задача — фильтровать логи по различным критериям (например, по пользователю или статусу) и отфильтровать их по условиям, которые можно изменять во время работы программы.
Шаг 1: Замыкания для динамической фильтрации
Начнём с простого фильтра. Мы хотим создать фильтр по пользователю. Для этого используем замыкание: функция фильтра будет запоминать, по какому пользователю мы фильтруем.
def filter_by_user(logs, user):
def filter_logs():
for log in logs:
if log["user"] == user:
yield log
return filter_logs
Здесь:
- filter_by_user — это функция, которая принимает список логов и пользователя.
- Внутри неё мы создаём другую функцию filter_logs(), которая фильтрует логи по заданному пользователю. Мы используем yield, чтобы вернуть логи по одному, не загружая всё в память.
Теперь можно использовать этот фильтр:
alice_filter = filter_by_user(logs, "alice")
for log in alice_filter():
print(log)
Результат:
{'user': 'alice', 'action': 'login', 'timestamp': '2025-12-01 10:00:00', 'status': 'success'}
{'user': 'alice', 'action': 'logout', 'timestamp': '2025-12-01 10:10:00', 'status': 'success'}
Что происходит:
- Мы создали замыкание, которое запоминает, по какому пользователю мы фильтруем.
- Логика фильтрации вынесена в отдельную функцию, которую можно вызвать несколько раз с разными пользователями.
Шаг 2: Декораторы для расширения функциональности
Теперь давайте добавим функциональность для фильтрации по статусу. Вместо того чтобы писать новый фильтр с нуля, мы используем декоратор, который будет добавлять фильтрацию по статусу к уже существующей фильтрации по пользователю.
Декоратор для фильтрации по статусу
def filter_by_status(filter_func):
def wrapper(logs, *args, **kwargs):
status = kwargs.get("status")
if status:
logs = [log for log in logs if log["status"] == status]
return filter_func(logs, *args, **kwargs)
return wrapper
Этот декоратор будет проверять, если в аргументах передан статус, и если он есть, фильтровать логи по статусу перед тем, как передать их в основную функцию фильтрации.
Декоратор для фильтрации по пользователю
📢 Подписывайтесь на наш Telegram-канал.
Там вы найдете анонсы обучающих статей и видео, готовый код для ваших проектов и увлекательные курсы. Ничего лишнего — только практика, вдохновение и развитие.
Теперь применим этот декоратор к функции фильтрации по пользователю:
@filter_by_status
def filter_by_user(logs, user):
for log in logs:
if log["user"] == user:
yield log
Теперь при вызове фильтра можно указать, что мы хотим фильтровать по статусу:
alice_filter = filter_by_user(logs, "alice", status="success")
for log in alice_filter():
print(log)
Результат:
{'user': 'alice', 'action': 'login', 'timestamp': '2025-12-01 10:00:00', 'status': 'success'}
{'user': 'alice', 'action': 'logout', 'timestamp': '2025-12-01 10:10:00', 'status': 'success'}
Теперь функция фильтрации по пользователю расширена возможностью фильтровать по статусу с помощью декоратора.
Шаг 3: Генераторы для обработки больших объёмов данных
Допустим, логи могут быть очень большими, и мы не можем держать их все в памяти. Для этого используем генераторы.
Мы уже использовали yield в функции filter_logs() для того, чтобы вернуть логи по одному, не занимая много памяти. Теперь давайте расширим это и используем генератор для обработки большого потока данных.
Генератор для обработки логов
def log_generator(logs):
for log in logs:
yield log
Теперь, чтобы фильтровать логи, мы можем просто обрабатывать их в цикле, не загружая весь список в память:
def process_logs(logs):
for log in log_generator(logs):
if log["status"] == "success":
print(log)
Здесь log_generator будет поочередно возвращать элементы, и мы будем их обрабатывать по одному. Это особенно полезно, когда данные очень большие, и вы не хотите загружать их все в память сразу.
Итоговый код
Теперь у нас есть система фильтрации логов с использованием замыканий, декораторов и генераторов:
# Пример данных
logs = [
{"user": "alice", "action": "login", "timestamp": "2025-12-01 10:00:00", "status": "success"},
{"user": "bob", "action": "login", "timestamp": "2025-12-01 10:05:00", "status": "failure"},
{"user": "alice", "action": "logout", "timestamp": "2025-12-01 10:10:00", "status": "success"},
{"user": "charlie", "action": "login", "timestamp": "2025-12-01 10:15:00", "status": "success"},
{"user": "bob", "action": "logout", "timestamp": "2025-12-01 10:20:00", "status": "failure"},
]
# Замыкание для фильтрации по пользователю
def filter_by_user(logs, user):
def filter_logs():
for log in logs:
if log["user"] == user:
yield log
return filter_logs
# Декоратор для фильтрации по статусу
def filter_by_status(filter_func):
def wrapper(logs, *args, **kwargs):
status = kwargs.get("status")
if status:
logs = [log for log in logs if log["status"] == status]
return filter_func(logs, *args, **kwargs)
return wrapper
# Применяем декоратор
@filter_by_status
def filter_by_user(logs, user):
for log in logs:
if log["user"] == user:
yield log
# Генератор для логов
def log_generator(logs):
for log in logs:
yield log
# Пример использования
alice_filter = filter_by_user(logs, "alice", status="success")
for log in alice_filter():
print(log)
Заключение
Мы разобрали три мощные концепции, которые делают работу с функциями ещё более удобной и гибкой:
- Замыкания: позволяют функции "помнить" параметры из внешней области видимости, создавая гибкие и динамичные фильтры.
- Декораторы: позволяют расширять функции, не меняя их исходного кода. Мы добавили фильтрацию по статусу.
- Генераторы: помогают работать с большими объёмами данных, обрабатывая их по одному элементу за раз, не загружая всё в память.
С помощью этих техник мы создали систему, которая легко фильтрует логи, расширяется новыми фильтрами и работает с большими данными без перегрузки памяти.
Практика на том же примере
- Добавьте новый фильтр по action (например, только login или logout).
- Создайте декоратор для проверки правильности формата временной метки.
- Добавьте новую фильтрацию, чтобы выводить только логи с определённым пользователем и статусом за определённый день (например, только для 2025-12-01).
Эти задачи помогут вам ещё глубже понять, как работает фильтрация и как использовать функции для обработки данных в реальных приложениях.
05.12.2025
0
40
Комментарии
0