Kodokon kodokon.com

Продвинутые структуры: кортежи, множества и collections

Выбирай структуру под каждую задачу: неизменяемые кортежи, множества для проверки вхождения за O(1), Counter и defaultdict для типовых сценариев.

9 мин · 3 вопросов

Открыть этот урок в Kodokon

Выбор правильной структуры данных одним движением решает и читаемость, и производительность. Кортеж - это неизменяемая последовательность: идеален для разнородной записи фиксированного размера (координаты, пара ключ-значение). Неизменяемость означает хешируемость (если содержимое тоже хешируемо): поэтому кортеж может служить ключом словаря или элементом множества, а список - нет. Распаковка делает код выразительным - а функция, которая «возвращает несколько значений», на самом деле возвращает кортеж.

PYTHON
point = (48.85, 2.35)
lat, lon = point

def min_max(values):
    return min(values), max(values)

low, high = min_max([3, 1, 4, 1, 5])
print(low, high)

distances = {("paris", "lyon"): 465}
print(distances[("paris", "lyon")])
Распаковка, множественный возврат и кортеж как ключ словаря

Множество гарантирует уникальность и даёт проверку вхождения за O(1) в среднем, тогда как у списка это O(n). В цикле, который тысячи раз проверяет x in collection, разница видна сразу. Операторы множеств (& пересечение, - разность, | объединение) - настоящий шаг вперёд по сравнению с вложенными циклами: сравнить два списка идентификаторов - кто исчез, кто новый - умещается в две строки.

PYTHON
active = {"ada", "linus", "guido"}
banned = {"linus", "mallory"}

print(active & banned)
print(active - banned)
print(active | banned)

emails = ["a@x.io", "b@x.io", "a@x.io"]
unique = set(emails)
print(len(unique))
Операции над множествами и удаление дубликатов

Модуль collections закрывает две повседневные потребности. Counter считает вхождения в итерируемом объекте и даёт most_common(n) - больше никаких самописных словарей-счётчиков. defaultdict(factory) создаёт недостающее значение на лету: defaultdict(list) - канонический инструмент для группировки элементов по ключу без проверки существования ключа на каждом проходе цикла.

PYTHON
from collections import Counter, defaultdict

words = ["go", "py", "go", "rs", "go"]
counts = Counter(words)
print(counts.most_common(2))

groups = defaultdict(list)
pairs = [("fr", "Ada"), ("us", "Lin"), ("fr", "Zoe")]
for country, name in pairs:
    groups[country].append(name)
print(dict(groups))
Counter, чтобы считать; defaultdict, чтобы группировать

Проверка знаний

Убедись, что запомнил ключевые моменты этого урока.

  1. Почему кортеж может быть ключом словаря, а список - нет?
    • Он быстрее создаётся
    • Он неизменяемый, а значит хешируемый
    • Он может содержать только числа
    • Словари сами превращают списки в кортежи
  2. Ты тысячи раз проверяешь x in collection на 100 000 элементах. Какую структуру выбрать?
    • Список, просматриваемый последовательно
    • Множество, вхождение за O(1) в среднем
    • Кортеж, более компактный, чем список
  3. С groups = defaultdict(list) что делает чтение отсутствующего ключа?
    • Выбрасывает KeyError
    • Возвращает пустой список и вставляет ключ
    • Возвращает None, не изменяя словарь