Всё — объекты, переменные — имена
В 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, None | list, 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 |
OrderedDict | move_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 и синглтонов, значения сравнивают через==.