Урок 01 из 12 · 2 минуты чтения

Типы и коллекции — что изменяемо, что хешируемо и как устроен dict

Изменяемые и неизменяемые типы, список и кортеж, словарь и множество на хеш-таблице, is против ==, модуль collections.

Всё — объекты, переменные — имена

В Python переменная — не ячейка с значением, а имя, привязанное к объекту. b = a не копирует объект, а даёт ему второе имя:

a = [1, 2, 3]
b = a
b.append(4)
print(a)  # [1, 2, 3, 4]

Отсюда главное разделение типов.

Изменяемые и неизменяемые

НеизменяемыеИзменяемые
int, float, bool, str, bytes, tuple, frozenset, Nonelist, dict, set, bytearray, объекты обычных классов

Изменяемый объект меняется на месте, все имена видят изменение. Неизменяемый можно только заменить новым: s += "!" создаёт новую строку.

С изменяемостью связана хешируемость: ключом словаря и элементом множества может быть только объект, хеш которого не меняется за время жизни. Поэтому dict[(1, 2)] можно, а dict[[1, 2]] — нет.

Список и кортеж

Список изменяемый, кортеж — нет; остальные различия следуют из этого. Кортеж может быть ключом словаря (если хешируемы его элементы), чуть легче по памяти и быстрее создаётся. По смыслу список — набор однотипных элементов, кортеж — запись с фиксированной структурой: (name, age).

Словарь — хеш-таблица

По hash(key) словарь находит место ключа, поэтому поиск, вставка и удаление — в среднем O(1). При коллизиях — разных ключах с одним местом — поиск может деградировать до O(n), но на практике это редкость. С Python 3.7 словарь гарантированно сохраняет порядок вставки.

Множество

set — набор уникальных хешируемых элементов на той же хеш-таблице. Проверка x in s — в среднем O(1), у списка — O(n). Это частая оптимизация: проверка вхождения в цикле по списку даёт O(n²), по множеству — O(n).

seen = set(ids)
a | b   # объединение
a & b   # пересечение
a - b   # разность

frozenset — неизменяемая версия, её можно положить в другое множество.

is и ==

== сравнивает значения (вызывает __eq__), is — что это один и тот же объект.

[1, 2] == [1, 2]   # True
[1, 2] is [1, 2]   # False — два разных списка
x is None          # правильная проверка на None

is используют для None, True, False и объектов-сентинелов. Числа и строки через is не сравнивают: результат зависит от деталей реализации.

Модуль collections

ТипКогда
defaultdict(list)группировка без проверки «есть ли ключ»
Counterподсчёт, most_common(10)
dequeочередь с O(1) с обоих концов, скользящее окно через maxlen
OrderedDictmove_to_end, равенство с учётом порядка — нужен редко
from collections import Counter, deque
Counter("abracadabra").most_common(2)  # [('a', 5), ('b', 2)]
q = deque(maxlen=3)

list.pop(0) — O(n), deque.popleft() — O(1). У defaultdict есть ловушка: даже чтение d[k] создаёт ключ.

Что запомнить

  • Присваивание не копирует — даёт объекту ещё одно имя.
  • Ключом словаря может быть только хешируемое, то есть неизменяемое.
  • dict и set — хеш-таблицы с поиском O(1).
  • is — только для None и синглтонов, значения сравнивают через ==.

Проверка урока

Войдите, чтобы проверить себя: сданные проверки всех уроков курса отмечают его темы пройденными на карте и в ИПР.

Вопросы по теме

Читайте также

Партнёр платформы

Разбор собеседования с sotto

После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.

Разбор собеседования