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

Что использовалось из модуля collections на проектах?

2.3 Middle🔥 131 комментариев
#Python Core

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

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

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

Применение модуля Collections в проектах

Модуль collections в Python предоставляет специализированные контейнеры данных, которые часто используются в реальных проектах. Расскажу о практическом применении каждого.

1. Counter — подсчёт элементов

Counter используется для подсчёта частоты элементов в последовательности.

from collections import Counter

# Подсчёт символов в тексте
text = "hello world"
char_count = Counter(text)
print(char_count)  # Counter({'l': 3, 'o': 2, 'h': 1, ...})

# Получение топ элементов
print(char_count.most_common(3))  # [('l', 3), ('o', 2), ('h', 1)]

# Практический пример: анализ логов
log_data = ["error", "warning", "error", "info", "error", "warning"]
error_counts = Counter(log_data)
print(error_counts)  # Counter({'error': 3, 'warning': 2, 'info': 1})

# Получить самую частую ошибку
most_common_error = error_counts.most_common(1)[0][0]
print(f"Самая частая ошибка: {most_common_error}")

# Арифметика с Counter
count1 = Counter({'a': 3, 'b': 1})
count2 = Counter({'a': 1, 'b': 2})

print(count1 + count2)  # Counter({'a': 4, 'b': 3})
print(count1 - count2)  # Counter({'a': 2})
print(count1 & count2)  # Counter({'a': 1, 'b': 1})
print(count1 | count2)  # Counter({'a': 3, 'b': 2})

2. defaultdict — словарь с дефолтным значением

Используется для избежания KeyError при работе со словарями.

from collections import defaultdict

# Без defaultdict (опасно)
try:
    d = {}
    d['key'].append('value')  # KeyError
except KeyError:
    print("Нужно проверять наличие ключа")

# С defaultdict (безопасно)
from collections import defaultdict

# Группировка элементов
data = [("apple", 5), ("banana", 3), ("apple", 2), ("cherry", 1)]
fruit_counts = defaultdict(int)

for fruit, count in data:
    fruit_counts[fruit] += count

print(dict(fruit_counts))  # {'apple': 7, 'banana': 3, 'cherry': 1}

# Список значений по ключу
students_by_class = defaultdict(list)
students_data = [("10A", "Alice"), ("10B", "Bob"), ("10A", "Charlie")]

for class_name, student in students_data:
    students_by_class[class_name].append(student)

print(dict(students_by_class))
# {'10A': ['Alice', 'Charlie'], '10B': ['Bob']}

# Вложенные defaultdict
from collections import defaultdict

matrix = defaultdict(lambda: defaultdict(int))
matrix[1][1] = 5
matrix[1][2] = 3
print(matrix[1][1])  # 5
print(matrix[2][3])  # 0 (по умолчанию)

3. OrderedDict — упорядоченный словарь

В Python 3.7+ обычные словари уже сохраняют порядок, но OrderedDict полезен для обратной совместимости и специальных операций.

from collections import OrderedDict

# Сохранение порядка вставки
ordered = OrderedDict()
ordered['z'] = 1
ordered['a'] = 2
ordered['m'] = 3

print(list(ordered.keys()))  # ['z', 'a', 'm'] - порядок сохранён

# Перемещение элемента в конец
ordered.move_to_end('a')
print(list(ordered.keys()))  # ['z', 'm', 'a']

# Перемещение в начало
ordered.move_to_end('a', last=False)
print(list(ordered.keys()))  # ['a', 'z', 'm']

# Удаление последнего элемента
last_key, last_value = ordered.popitem()
print(f"Удалён: {last_key} = {last_value}")  # Удалён: m = 3

# Практический пример: кэш с ограничением размера
class LRUCache(OrderedDict):
    def __init__(self, max_size=100):
        self.max_size = max_size
        super().__init__()
    
    def __getitem__(self, key):
        value = super().__getitem__(key)
        # Перемещаем в конец (самый свежий)
        self.move_to_end(key)
        return value
    
    def __setitem__(self, key, value):
        if key in self:
            # Обновляем существующий
            self.move_to_end(key)
        super().__setitem__(key, value)
        
        # Удаляем старый элемент если превышен размер
        if len(self) > self.max_size:
            oldest = next(iter(self))
            del self[oldest]

# Тест кэша
cache = LRUCache(max_size=3)
cache['a'] = 1
cache['b'] = 2
cache['c'] = 3
print(list(cache.keys()))  # ['a', 'b', 'c']

cache['d'] = 4  # Добавляем четвёртый
print(list(cache.keys()))  # ['b', 'c', 'd'] - 'a' удалён

4. namedtuple — кортеж с именованными полями

Проще и быстрее чем классы для простых структур данных.

from collections import namedtuple

# Определение структуры
Point = namedtuple('Point', ['x', 'y'])
p = Point(3, 4)

print(p.x, p.y)  # 3 4
print(p[0], p[1])  # 3 4 (доступ по индексу)

# Более практический пример: данные пользователя
User = namedtuple('User', ['id', 'name', 'email', 'age'])

users = [
    User(1, 'Alice', 'alice@example.com', 30),
    User(2, 'Bob', 'bob@example.com', 25),
    User(3, 'Charlie', 'charlie@example.com', 35),
]

for user in users:
    print(f"{user.name} ({user.age} лет) - {user.email}")

# Преимущества namedtuple:
print(f"\nСлужебная информация:")
print(f"Поля: {User._fields}")  # ('id', 'name', 'email', 'age')

# Создание из словаря
user_dict = {'id': 4, 'name': 'David', 'email': 'david@example.com', 'age': 28}
user = User(**user_dict)
print(user)  # User(id=4, name='David', email='david@example.com', age=28)

# Преобразование обратно
user_dict_back = user._asdict()
print(user_dict_back)  # OrderedDict([('id', 4), ('name', 'David'), ...])

# Замена значения (неизменяемость)
user_updated = user._replace(age=29)
print(user_updated)  # User(id=4, name='David', ..., age=29)
print(user)  # Исходный не изменился

5. deque — двусторонняя очередь

Оптимизирована для быстрых вставок и удалений с обоих концов.

from collections import deque

# Создание deque
q = deque([1, 2, 3, 4, 5])

# Операции с конца
q.append(6)  # Добавить в конец
q.appendleft(0)  # Добавить в начало
print(q)  # deque([0, 1, 2, 3, 4, 5, 6])

right = q.pop()  # Удалить с конца
left = q.popleft()  # Удалить с начала
print(f"Удалено: {left} и {right}")  # Удалено: 0 и 6

# Ротация
q = deque([1, 2, 3, 4, 5])
q.rotate(2)  # Повернуть на 2 позиции
print(q)  # deque([4, 5, 1, 2, 3])

q.rotate(-1)  # Повернуть в другую сторону
print(q)  # deque([5, 1, 2, 3, 4])

# Практический пример: скользящее окно
def sliding_window(iterable, window_size):
    """Скользящее окно с фиксированным размером."""
    it = iter(iterable)
    window = deque(maxlen=window_size)
    
    # Заполняем первое окно
    for _ in range(window_size):
        try:
            window.append(next(it))
        except StopIteration:
            return
    
    yield list(window)
    
    # Скользим окно
    for item in it:
        window.append(item)  # Старый элемент автоматически удаляется
        yield list(window)

# Тест
data = range(10)
for window in sliding_window(data, 3):
    print(window)
# [0, 1, 2]
# [1, 2, 3]
# [2, 3, 4]
# ...

# Пример: очередь задач
task_queue = deque()
task_queue.append("task1")
task_queue.append("task2")
task_queue.append("task3")

while task_queue:
    task = task_queue.popleft()
    print(f"Выполняю: {task}")

6. ChainMap — объединение нескольких словарей

Для работы с несколькими словарями как с одним.

from collections import ChainMap

# Объединение конфигураций
default_config = {'debug': False, 'port': 8000}
user_config = {'debug': True}
command_line_args = {'port': 9000}

# ChainMap ищет в порядке приоритета
config = ChainMap(command_line_args, user_config, default_config)

print(config['debug'])  # True (из user_config)
print(config['port'])   # 9000 (из command_line_args)

# Все ключи (объединённые)
print(list(config.keys()))  # ['port', 'debug']
print(dict(config))  # {'debug': True, 'port': 9000}

# Практический пример: окружение переменных
import os
from collections import ChainMap

# Приоритет: переменные окружения > значения по умолчанию
defaults = {'LOG_LEVEL': 'INFO', 'DB_HOST': 'localhost'}
env = ChainMap(os.environ, defaults)

log_level = env.get('LOG_LEVEL')
db_host = env.get('DB_HOST')

Сравнение производительности

import time
from collections import Counter, defaultdict

# Counter vs defaultdict для подсчёта
data = ['a'] * 1000 + ['b'] * 500 + ['c'] * 200

start = time.time()
for _ in range(10000):
    c = Counter(data)
time_counter = time.time() - start

start = time.time()
for _ in range(10000):
    d = defaultdict(int)
    for item in data:
        d[item] += 1
time_defaultdict = time.time() - start

print(f"Counter: {time_counter:.3f}s")
print(f"defaultdict: {time_defaultdict:.3f}s")

Практический пример: анализ веб-логов

from collections import Counter, defaultdict
import re
from datetime import datetime

class LogAnalyzer:
    def __init__(self, log_file):
        self.logs = self.parse_logs(log_file)
    
    def parse_logs(self, log_file):
        logs = []
        with open(log_file) as f:
            for line in f:
                # Простой парсинг
                match = re.match(r'(\d+\.\d+\.\d+\.\d+).*"(\w+) (.*?) HTTP.*" (\d+)', line)
                if match:
                    ip, method, path, status = match.groups()
                    logs.append({
                        'ip': ip,
                        'method': method,
                        'path': path,
                        'status': int(status),
                    })
        return logs
    
    def analyze(self):
        # Частые IP адреса
        ips = Counter(log['ip'] for log in self.logs)
        print(f"Топ IP: {ips.most_common(3)}")
        
        # Ошибки по типу
        errors = defaultdict(list)
        for log in self.logs:
            if log['status'] >= 400:
                errors[log['status']].append(log)
        
        print(f"Ошибки: {dict(errors)}")
        
        # Популярные пути
        paths = Counter(log['path'] for log in self.logs)
        print(f"Топ пути: {paths.most_common(5)}")

# analyzer = LogAnalyzer('access.log')
# analyzer.analyze()

Заключение

Основные модули collections и их применение:

КлассПрименение
CounterПодсчёт частоты элементов, анализ данных
defaultdictБезопасная работа со словарями, группировка
OrderedDictСохранение порядка, LRU кэши
namedtupleСтруктуры данных вместо классов
dequeОчереди, скользящие окна
ChainMapОбъединение конфигов, приоритеты

Эти структуры значительно упрощают код и повышают производительность в реальных проектах.

Что использовалось из модуля collections на проектах? | PrepBro