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

Зачем нужна коллекция в Python?

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

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

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

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

Назначение коллекций (Collections) в Python

Модуль collections предоставляет специализированные контейнеры, которые более эффективны и удобны, чем встроенные типы (list, dict, set, tuple) для конкретных задач.

Основная проблема

Встроенные типы — не всегда идеальны

# Проблема 1: Работа с очередью
queue = []

# Добавляем в конец
queue.append(1)
queue.append(2)
queue.append(3)

# Берём из начала — неэффективно!
first = queue.pop(0)  # O(n) — все элементы сдвигаются!
print(first)  # 1

# Проблема: при 1000 элементов pop(0) очень медленно

# Проблема 2: Словарь, который помнит порядок вставки
old_dict = {}
old_dict['z'] = 1
old_dict['a'] = 2
old_dict['m'] = 3
# В Python < 3.7 порядок был случайным
# В Python >= 3.7 порядок гарантирован, но это не явно видно

# Проблема 3: Счёт элементов
data = [1, 1, 1, 2, 2, 3, 3, 3, 3]
counts = {}
for item in data:
    if item in counts:
        counts[item] += 1
    else:
        counts[item] = 1
print(counts)  # {1: 3, 2: 2, 3: 4}
# Много кода для простой операции

Решение: Collections

from collections import deque, defaultdict, Counter, OrderedDict, namedtuple

# Вот как это становится просто

Основные типы из Collections

1. deque (Double-Ended Queue)

Для чего: очереди и стеки с O(1) операциями с обоих концов

from collections import deque

# Проблема: очередь с list
queue_list = []
for i in range(1000):
    queue_list.append(i)  # O(1)
for i in range(1000):
    queue_list.pop(0)  # O(n) — медленно!

# Решение: deque
queue = deque()
for i in range(1000):
    queue.append(i)  # O(1)
for i in range(1000):
    item = queue.popleft()  # O(1) — быстро!

# Реальный пример: обработка задач
tasks = deque(["task1", "task2", "task3"])

while tasks:
    task = tasks.popleft()  # Берём первый
    print(f"Processing {task}")
    
    if task == "task1":
        tasks.append("subtask1")  # Добавляем новый в очередь

2. Counter

Для чего: подсчёт элементов

from collections import Counter

# Проблема: подсчёт вручную
words = ["apple", "banana", "apple", "cherry", "banana", "apple"]
word_count = {}
for word in words:
    word_count[word] = word_count.get(word, 0) + 1
print(word_count)  # {"apple": 3, "banana": 2, "cherry": 1}

# Решение: Counter
word_count = Counter(words)
print(word_count)  # Counter({'apple': 3, 'banana': 2, 'cherry': 1})

# Бонус: most_common()
most_frequent = word_count.most_common(2)
print(most_frequent)  # [('apple', 3), ('banana', 2)]

# Реальный пример: аналитика
logs = [
    "user_123_logged_in",
    "user_456_logged_in",
    "user_123_purchased",
    "user_789_logged_in",
    "user_123_logged_out",
]

event_types = Counter([log.split('_')[-1] for log in logs])
print(event_types)
# Counter({'in': 3, 'purchased': 1, 'out': 1})

most_common_event = event_types.most_common(1)
print(f"Most common: {most_common_event[0][0]}")
# Most common: in

3. defaultdict

Для чего: словарь со значениями по умолчанию

from collections import defaultdict

# Проблема: KeyError при доступе к несуществующему ключу
students = {}
students['Alice'] = [90, 85]
students['Bob'] = [75, 80]

score = students['Charlie']  # KeyError!

# Решение 1: проверка
if 'Charlie' in students:
    score = students['Charlie']
else:
    score = []

# Решение 2: get()
score = students.get('Charlie', [])

# Решение 3: defaultdict (самое красивое)
students = defaultdict(list)  # Заранее говорим, что по умолчанию list

students['Alice'].append(90)
students['Alice'].append(85)
students['Bob'].append(75)

# Автоматически создаёт пустой list для новых студентов
students['Charlie'].append(95)  # Работает!

print(dict(students))
# {'Alice': [90, 85], 'Bob': [75], 'Charlie': [95]}

# Реальный пример: группировка данных
from collections import defaultdict

orders = [
    {'user_id': 1, 'amount': 100},
    {'user_id': 2, 'amount': 150},
    {'user_id': 1, 'amount': 50},
    {'user_id': 3, 'amount': 200},
]

user_orders = defaultdict(list)
for order in orders:
    user_orders[order['user_id']].append(order['amount'])

print(dict(user_orders))
# {1: [100, 50], 2: [150], 3: [200]}

# Вычисляем сумму по пользователям
user_totals = defaultdict(float)
for order in orders:
    user_totals[order['user_id']] += order['amount']

print(dict(user_totals))
# {1: 150.0, 2: 150.0, 3: 200.0}

4. namedtuple

Для чего: создание простых immutable классов

from collections import namedtuple

# Проблема: много кода для простого объекта
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __repr__(self):
        return f"Point(x={self.x}, y={self.y})"

point = Point(10, 20)
print(point.x)  # 10

# Решение: namedtuple (одна строка!)
Point = namedtuple('Point', ['x', 'y'])
point = Point(10, 20)

print(point.x)  # 10
print(point[0])  # 10 (можно как tuple)
print(point)  # Point(x=10, y=20)

# Бонусы
x, y = point  # Распаковка как tuple
point_dict = point._asdict()  # Преобразование в dict
print(point_dict)  # {'x': 10, 'y': 20}

# Реальный пример: результаты поиска
SearchResult = namedtuple('SearchResult', ['id', 'title', 'score'])

results = [
    SearchResult(1, "Python Guide", 0.95),
    SearchResult(2, "Python Tutorial", 0.87),
    SearchResult(3, "Java Guide", 0.75),
]

for result in results:
    print(f"ID: {result.id}, Title: {result.title}, Score: {result.score}")

# Сортировка по score
top_result = max(results, key=lambda r: r.score)
print(f"Best match: {top_result.title}")

5. OrderedDict

Для чего: явное сохранение порядка (в Python < 3.7)

from collections import OrderedDict

# В Python 3.7+ обычный dict тоже сохраняет порядок
# Но OrderedDict имеет дополнительные методы

config = OrderedDict()
config['host'] = 'localhost'
config['port'] = 8000
config['debug'] = True

for key, value in config.items():
    print(f"{key}: {value}")  # Гарантированный порядок

# Метод move_to_end() — уникален для OrderedDict
config.move_to_end('debug', last=False)  # Переместить в начало
print(list(config.keys()))  # ['debug', 'host', 'port']

6. ChainMap

Для чего: объединение нескольких словарей

from collections import ChainMap

# Сценарий: настройки с fallback values
defaults = {'host': 'localhost', 'port': 8000, 'debug': False}
env_vars = {'host': '0.0.0.0', 'port': 3000}
user_config = {'debug': True}

# Без ChainMap: нужно вручную объединять
config = {**defaults, **env_vars, **user_config}
print(config)  # {'host': '0.0.0.0', 'port': 3000, 'debug': True}

# С ChainMap: создаём цепочку приоритета
config = ChainMap(user_config, env_vars, defaults)

print(config['host'])    # '0.0.0.0' (из env_vars, т.к. приоритет выше)
print(config['debug'])   # True (из user_config, наивысший приоритет)
print(config['nonexistent'])  # KeyError (не во всех словарях)
print(config.get('nonexistent'))  # None

# Реальный пример: конфигурация приложения
app_config = ChainMap(
    os.environ,           # Приоритет 1: environment variables
    user_settings,        # Приоритет 2: пользовательские настройки
    defaults,             # Приоритет 3: значения по умолчанию
)

Когда использовать что

КоллекцияКогда использоватьГлавное преимущество
dequeОчереди, стекиO(1) пушинг/попинг с обоих концов
CounterПодсчёт элементовПростой и быстрый подсчёт
defaultdictГруппировка, аккумуляцияНет KeyError, читаемый код
namedtupleПростые структуры данныхИммутабелен, синтаксис как класс
OrderedDictЕсли нужны методы вроде move_to_endЯвная семантика порядка
ChainMapНастройки с fallbackElegantly merged lookup

Пример использования вместе

from collections import deque, Counter, defaultdict

# Обработка логов
logs = deque(open('app.log').readlines())  # Читаем в очередь
error_counts = Counter()  # Подсчитываем ошибки
errors_by_user = defaultdict(list)  # Группируем по пользователям

while logs:
    log_line = logs.popleft()  # Берём из очереди эффективно
    
    if 'ERROR' in log_line:
        error_type = log_line.split('ERROR: ')[1].split('\n')[0]
        error_counts[error_type] += 1
        
        user_id = log_line.split('USER: ')[1].split(' ')[0]
        errors_by_user[user_id].append(error_type)

print("Most common errors:", error_counts.most_common(3))
print("Errors by user:", dict(errors_by_user))

Вывод

Коллекции из модуля collections — это essential tools для написания:

  • Более читаемого кода
  • Более эффективного кода
  • Более Pythonic кода

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