Collections в Python: 10 практичных структур

в 10:10, , рубрики: collections, data structures, python, python3
Collections в Python: 10 практичных структур - 1

Стандартные типы данных 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

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js