Изучаем модуль collections в Python
Модуль collections предоставляет специализированные контейнерные типы данных, которые являются альтернативами встроенным контейнерам (list, dict, set, tuple). Эти типы оптимизированы для конкретных задач и часто более эффективны.
Содержание
1. Counter
Счетчик (Counter) — это подкласс словаря для подсчета хешируемых объектов.
Основное использование:
from collections import Counter
# Создание Counter
words = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple']
word_count = Counter(words)
print(word_count) # Counter({'apple': 3, 'banana': 2, 'orange': 1})
# Подсчет символов в строке
char_count = Counter("abracadabra")
print(char_count) # Counter({'a': 5, 'b': 2, 'r': 2, 'c': 1, 'd': 1})
Полезные методы:
# most_common() - n самых частых элементов
print(word_count.most_common(2)) # [('apple', 3), ('banana', 2)]
# elements() - итератор по элементам
print(list(word_count.elements())) # ['apple', 'apple', 'apple', 'banana', 'banana', 'orange']
# Арифметические операции
c1 = Counter(a=3, b=1)
c2 = Counter(a=1, b=2)
print(c1 + c2) # Counter({'a': 4, 'b': 3})
print(c1 - c2) # Counter({'a': 2})
2. defaultdict
defaultdict — это подкласс словаря, который вызывает factory-функцию для отсутствующих ключей.
Пример:
from collections import defaultdict
# Создание defaultdict с list как factory-функцией
dd = defaultdict(list)
# При обращении к отсутствующему ключу создается пустой список
dd['fruits'].append('apple')
dd['fruits'].append('banana')
dd['vegetables'].append('carrot')
print(dd)
# defaultdict(<class 'list'>, {'fruits': ['apple', 'banana'], 'vegetables': ['carrot']})
# С lambda-функцией
dd_int = defaultdict(lambda: 0)
dd_int['count'] += 1
print(dd_int['count']) # 1
print(dd_int['missing']) # 0 (автоматически создается)
Практический пример:
# Группировка слов по длине
words = ['apple', 'bat', 'cat', 'orange', 'dog', 'elephant']
word_by_length = defaultdict(list)
for word in words:
word_by_length[len(word)].append(word)
print(dict(word_by_length))
# {5: ['apple', 'orange'], 3: ['bat', 'cat', 'dog'], 8: ['elephant']}
3. deque
deque (двусторонняя очередь) — оптимизированная структура для быстрого добавления/удаления с обоих концов.
Основные операции:
from collections import deque
# Создание deque
d = deque([1, 2, 3])
print(d) # deque([1, 2, 3])
# Добавление элементов
d.append(4) # справа
d.appendleft(0) # слева
print(d) # deque([0, 1, 2, 3, 4])
# Удаление элементов
d.pop() # справа
d.popleft() # слева
print(d) # deque([1, 2, 3])
# Вращение
d.rotate(1) # вправо на 1
print(d) # deque([3, 1, 2])
d.rotate(-1) # влево на 1
print(d) # deque([1, 2, 3])
# Ограничение максимальной длины
d_max = deque(maxlen=3)
for i in range(5):
d_max.append(i)
print(d_max)
# deque([0], maxlen=3)
# deque([0, 1], maxlen=3)
# deque([0, 1, 2], maxlen=3)
# deque([1, 2, 3], maxlen=3) - вытеснился 0
# deque([2, 3, 4], maxlen=3) - вытеснился 1
4. namedtuple
namedtuple создает подкласс tuple с именованными полями.
Создание и использование:
from collections import namedtuple
# Создание namedtuple
Point = namedtuple('Point', ['x', 'y'])
Color = namedtuple('Color', 'red green blue')
# Создание экземпляров
p = Point(10, 20)
c = Color(255, 0, 0)
print(p.x, p.y) # 10 20
print(c.red) # 255
# Доступ по индексу (как обычный tuple)
print(p[0]) # 10
# Неизменяемость (как tuple)
# p.x = 100 # Ошибка: AttributeError
# Дополнительные возможности
print(p._asdict()) # {'x': 10, 'y': 20}
print(p._fields) # ('x', 'y')
# Замена значений (создает новый экземпляр)
p2 = p._replace(x=100)
print(p2) # Point(x=100, y=20)
С параметром defaults (Python 3.7+):
Person = namedtuple('Person', ['name', 'age', 'city'], defaults=['Moscow', 25])
p1 = Person('Alice') # age=25, city='Moscow' по умолчанию
print(p1) # Person(name='Alice', age=25, city='Moscow')
5. OrderedDict
OrderedDict — словарь, который сохраняет порядок добавления элементов (в Python 3.7+ обычные dict тоже сохраняют порядок, но OrderedDict имеет дополнительные методы).
Особенности:
from collections import OrderedDict
# Создание OrderedDict
od = OrderedDict()
od['a'] = 1
od['c'] = 3
od['b'] = 2
print(od) # OrderedDict([('a', 1), ('c', 3), ('b', 2)])
# Перемещение элемента в конец
od.move_to_end('a')
print(od) # OrderedDict([('c', 3), ('b', 2), ('a', 1)])
# Перемещение элемента в начало
od.move_to_end('b', last=False)
print(od) # OrderedDict([('b', 2), ('c', 3), ('a', 1)])
# popitem() - удаление последнего/первого элемента
last = od.popitem()
print(last) # ('a', 1)
first = od.popitem(last=False)
print(first) # ('b', 2)
6. ChainMap
ChainMap объединяет несколько словарей в одно представление.
Использование:
from collections import ChainMap
dict1 = {'a': 1, 'b': 2}
dict2 = {'b': 3, 'c': 4}
dict3 = {'d': 5}
# Создание ChainMap
chain = ChainMap(dict1, dict2, dict3)
# Поиск идет по цепочке слева направо
print(chain['a']) # 1 (из dict1)
print(chain['b']) # 2 (из dict1, первый найденный)
print(chain['c']) # 4 (из dict2)
print(chain['d']) # 5 (из dict3)
# Изменения затрагивают только первый словарь
chain['b'] = 100
print(dict1) # {'a': 1, 'b': 100}
print(dict2) # {'b': 3, 'c': 4} (не изменился)
# Добавление нового контекста
new_chain = chain.new_child({'e': 6})
print(new_chain) # ChainMap({'e': 6}, {'a': 1, 'b': 100}, {'b': 3, 'c': 4}, {'d': 5})
Практический пример (конфигурация):
defaults = {'theme': 'light', 'language': 'en'}
user_settings = {'theme': 'dark'}
system_env = {'language': 'ru'}
config = ChainMap(user_settings, system_env, defaults)
print(config['theme']) # 'dark' (из user_settings)
print(config['language']) # 'ru' (из system_env)
Практические задачи
Задача 1: Анализ текста
from collections import Counter
import re
def analyze_text(text):
# Приведение к нижнему регистру и удаление знаков препинания
words = re.findall(r'\b\w+\b', text.lower())
word_counter = Counter(words)
print("10 самых частых слов:")
for word, count in word_counter.most_common(10):
print(f"{word}: {count}")
print(f"\nВсего уникальных слов: {len(word_counter)}")
print(f"Всего слов: {sum(word_counter.values())}")
text = "Hello world! Hello Python. Python is great. World of Python."
analyze_text(text)
Задача 2: Кэш LRU (Least Recently Used)
from collections import OrderedDict
class LRUCache:
def __init__(self, capacity):
self.cache = OrderedDict()
self.capacity = capacity
def get(self, key):
if key not in self.cache:
return -1
# Перемещаем в конец (самый недавно использованный)
self.cache.move_to_end(key)
return self.cache[key]
def put(self, key, value):
if key in self.cache:
self.cache.move_to_end(key)
self.cache[key] = value
if len(self.cache) > self.capacity:
# Удаляем самый старый элемент
self.cache.popitem(last=False)
# Использование
cache = LRUCache(2)
cache.put(1, 'a')
cache.put(2, 'b')
print(cache.get(1)) # 'a'
cache.put(3, 'c') # ключ 2 удаляется (превышение capacity)
print(cache.get(2)) # -1 (не найден)
Сводная таблица
| Коллекция | Назначение | Ключевые особенности |
|---|---|---|
| Counter | Подсчет элементов | Подсчет частот, арифметические операции |
| defaultdict | Словарь со значениями по умолчанию | Автоматическое создание отсутствующих ключей |
| deque | Двусторонняя очередь | Быстрые O(1) операции с обоих концов |
| namedtuple | Именованный кортеж | Чтение по именам, неизменяемость |
| OrderedDict | Упорядоченный словарь | Сохранение порядка, move_to_end |
| ChainMap | Цепочка словарей | Объединение словарей, поиск по цепочке |
Заключение
Модуль collections предоставляет мощные и оптимизированные структуры данных, которые упрощают решение многих задач. Выбирайте подходящую коллекцию в зависимости от ваших потребностей:
- Для подсчета элементов → Counter
- Для группировки данных → defaultdict
- Для очередей и стеков → deque
- Для читаемых структур данных → namedtuple
- Для упорядоченных словарей с дополнительными операциями → OrderedDict
- Для объединения контекстов → ChainMap