Стандартные типы данных Python вроде списков и словарей справляются с большинством задач. Но что делать, если нужно чуть больше гибкости? Например, подсчитать частоту слов, создать словарь, который сам знает, что делать с отсутствующими ключами, или реализовать очередь?
Модуль collections
— как та секретная полка на кухне: не обязательно пользоваться каждый день, но когда нужно, он творит чудеса. Здесь собраны продвинутые структуры данных, которые не просто облегчают жизнь, а делают её лучше.
Давайте разберём десять ключевых инструментов из collections
, чтобы вы больше не писали код с мыслями: "Да как же эти костыли сделать проще?".
1. Counter: Ваш карманный аналитик
Counter
— это суперинструмент для подсчёта. С его помощью можно легко узнать, сколько раз встречается тот или иной элемент.
Пример: Подсчёт слов в тексте
Допустим, у вас есть текст, и вы хотите узнать, какое слово встречается чаще всего.
from collections import Counter
text = "code code learn python python python"
word_counts = Counter(text.split())
print(word_counts)
# Output: Counter({'python': 3, 'code': 2, 'learn': 1})
Видите? Всё лаконично и без лишних движений.
Пример: Рейтинг символов
Какой символ самый популярный? Counter легко справится и с этим.
text = "abracadabra"
char_counts = Counter(text)
print(char_counts.most_common(2))
# Output: [('a', 5), ('b', 2)]
Когда использовать: Для анализа текстов, подсчёта логов или поиска самых частых значений в массиве данных.
2. defaultdict: "Зачем проверять, если можно не проверять?"
Если вам приходилось работать со словарями, то, наверняка, вы сталкивались с ситуацией, когда нужно проверить наличие ключа перед добавлением значений. С defaultdict
это больше не проблема.
Пример: Группировка студентов по курсам
Вы собираете студентов по их курсам, и defaultdict
автоматически создаёт пустой список, если ключ ещё не существует.
from collections import defaultdict
students = [
("Alice", "Math"),
("Bob", "Science"),
("Alice", "Science"),
("Bob", "Math"),
]
courses = defaultdict(list)
for student, course in students:
courses[course].append(student)
print(dict(courses))
# Output: {'Math': ['Alice', 'Bob'], 'Science': ['Bob', 'Alice']}
Что делает defaultdict
крутым: Он убирает из кода раздражающие проверки. Вместо этого он просто работает.
Без него ваш код выглядел бы так:
courses = {}
for student, course in students:
if course not in courses:
courses[course] = []
courses[course].append(student)
Да, тоже работает, но зачем усложнять, если можно сразу изящно?
3. deque: Быстрые очереди в Python
Работа с очередями или стеками? Забудьте про списки. deque
не просто быстрее, но и позволяет ограничивать длину, добавлять и удалять элементы как с начала, так и с конца.
Пример: Очередь задач
from collections import deque
tasks = deque(["task1", "task2", "task3"])
tasks.append("task4") # Добавляем в конец
completed_task = tasks.popleft() # Забираем с начала
print(tasks)
# Output: deque(['task2', 'task3', 'task4'])
print(completed_task)
# Output: task1
Пример: Ограниченная история
Что делать, если нужна очередь с ограничением длины?
history = deque(maxlen=3)
history.extend([1, 2, 3])
history.append(4)
print(history)
# Output: deque([2, 3, 4], maxlen=3)
Теперь очередь в магазине всегда под контролем.
4. namedtuple: Словари с точечным доступом
Когда хочется гибкости словаря, но при этом нужен доступ через точки, namedtuple
приходит на помощь.
Пример: Координаты точек
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
p = Point(10, 20)
print(p.x, p.y)
# Output: 10 20
Когда использовать: Передача данных между функциями, хранение настроек или упрощение работы с конфигурациями.
5. OrderedDict: На случай, если порядок решает
Сегодня и обычные словари сохраняют порядок, но OrderedDict
всё ещё полезен, если вы хотите сами управлять порядком или использовать его для специфичных задач, например, в более старых версиях языка.
Пример: Сортировка словаря
from collections import OrderedDict
data = {"b": 2, "a": 1, "c": 3}
sorted_data = OrderedDict(sorted(data.items()))
print(sorted_data)
# Output: OrderedDict([('a', 1), ('b', 2), ('c', 3)])
6. ChainMap: Работа с несколькими словарями одновременно
ChainMap
объединяет несколько словарей и позволяет искать ключи сразу в нескольких источниках. Это особенно полезно для работы с конфигурациями или при объединении нескольких пространств имён.
Пример: Объединение настроек
from collections import ChainMap
defaults = {"theme": "light", "version": 1.0}
user_settings = {"theme": "dark"}
config = ChainMap(user_settings, defaults)
print(config["theme"])
# Output: dark
print(config["version"])
# Output: 1.
Когда использовать: Если нужно искать данные в нескольких словарях, но не хочется их сливать в один. Кстати, если вы когда-то объединяли словари через dict.update()
, то ChainMap
— это как получить тот же результат, но без потери исходных данных.
7. UserDict: Создавайте свои словари
UserDict
— это удобная оболочка над словарём, которая позволяет легко создавать собственные его версии с кастомным поведением.
Пример: Словарь с изменёнными ключами
Допустим, вы хотите, чтобы ваш словарь всегда хранил ключи в верхнем регистре.
from collections import UserDict
class UppercaseDict(UserDict):
def __setitem__(self, key, value):
super().__setitem__(key.upper(), value)
my_dict = UppercaseDict()
my_dict["hello"] = "world"
print(my_dict)
# Output: {'HELLO': 'world'}
Когда использовать: Если вы хотите добавить специфическую логику для работы со словарём, например, проверку данных или автоматическое преобразование значений.
8. UserList: Списки, которые понимают вас
Если UserDict
работает со словарями, то UserList
— с массивами. Вы можете расширять поведение списков, добавляя нужную функциональность.
Пример: Список с ограничением уникальных элементов
Конкретно для этой цели можно использовать set
, но пример хороший.
from collections import UserList
class UniqueList(UserList):
def append(self, item):
if item not in self.data:
super().append(item)
unique_list = UniqueList()
unique_list.append(1)
unique_list.append(2)
unique_list.append(1)
print(unique_list)
# Output: [1, 2]
Когда использовать: Если вам нужен список с особым поведением, например, для работы с уникальными данными или валидацией.
9. UserString: Строки на стероидах
UserString
— это способ работать со строками, добавляя им новую функциональность.
Пример: Кастомный строчный класс
from collections import UserString
class UppercaseString(UserString):
def __init__(self, data):
super().__init__(data.upper())
upper_string = UppercaseString("hello world")
print(upper_string)
# Output: HELLO WORLD
Когда использовать: Если стандартные строки Python не дают вам достаточно гибкости для ваших задач.
10. PriorityQueue: Очереди с приоритетами (дополнительно)
Хотя модуль collections
напрямую не включает очереди с приоритетами, heapq
часто используется вместе с deque
для таких задач. Если вы хотите гибкости deque
и управления приоритетами, сочетайте их.
Пример: Простая очередь с приоритетами
import heapq
class PriorityQueue:
def __init__(self):
self.queue = []
def push(self, item, priority):
heapq.heappush(self.queue, (priority, item))
def pop(self):
return heapq.heappop(self.queue)[1]
pq = PriorityQueue()
pq.push("task1", 2)
pq.push("task2", 1)
pq.push("task3", 3)
print(pq.pop())
# Output: task2
Немного философии
Когда нужно решать задачи, стандартные инструменты Python хороши. Но collections
даёт вам то, что помогает не просто "решать", а решать красиво, с минимумом усилий и без лишнего кода. Это как работать с хорошими инструментами: результат тот же, но процесс приятнее.
Так что, если вы когда-нибудь подумаете: "Почему так хочется сделать костыль?", вспомните collections
.
И, кстати, если вы до сих пор вручную проверяете наличие ключей в словарях, пора это исправить.
P.S. Если вам ещё нужны причины попробовать collections
, подумайте о том, сколько кода можно сэкономить. А на сэкономленное время можно выпить кофе. Или два.
Автор: CodeDict