Типы данных
В Python нет «переменных-коробок». Есть объекты в куче и имена, связанные с ними ссылками. Присваивание не копирует объект — оно привязывает ещё одно имя к тому же самому объекту, и всё поведение встроенных типов выводится из этой модели. Объект либо изменяемый (list, dict, set), и тогда правка через одно имя видна через все остальные, либо неизменяемый (int, str, tuple, frozenset), и тогда любая «правка» на деле создаёт новый объект и перепривязывает имя.
Второе разделение — хешируемость. Ключ dict и элемент set обязаны иметь стабильный hash, согласованный с __eq__; поэтому изменяемые типы туда не годятся, а хеш-таблица ищет за O(1) там, где список честно перебирает элементы за O(n). Третья ось — представление данных. float хранит двоичную дробь, поэтому 0.1 + 0.2 не равно 0.3; str хранит кодовые точки Unicode, а не байты и не «символы» в человеческом смысле. Ловушки темы — is вместо ==, алиасинг вложенных списков, мутация ключа после вставки, bool как подкласс int — разобраны в слоях ниже.
Карта темы
- Последовательности — что делает тип последовательностью — порядок,
__getitem__по целому индексу и__len__. - Срезы —
s[start:stop:step]возвращает новый объект, границы обрезаются, а не падают. - Отрицательные индексы —
-1это последний элемент, и почему это не «обратный отсчёт с ошибкой на единицу». - Изменяемость — изменение на месте против перепривязки имени и почему это разные операции.
- list против tuple — не только «нельзя менять» — разная память, разная стратегия роста, разная хешируемость.
- Распаковка кортежей — позиционное связывание, звёздочный остаток и вложенные цели.
- Хешируемость — контракт
__hash__и__eq__и почему изменяемый объект его нарушает. - Хеш-таблица изнутри — разреженная таблица CPython, пробирование при коллизии и ресайз.
- Ключи словаря — что годится в ключи, и что случится, если изменить ключ после вставки.
- Отображения — интерфейс
Mapping,get/setdefault,defaultdictиCounterвместоKeyError. - Представления словаря —
keys/values/itemsэто живые окна, а не снимки. - Множества — уникальность через хеш, операции над множествами и
frozensetкак ключ. - Тождество — is против == — кэш малых целых и интернирование строк делают
isобманчиво «рабочим». - Ссылки и алиасинг —
b = aне копирует,[[0]*3]*3создаёт три ссылки на один список. - Сравнение последовательностей — лексикографический порядок поэлементно и когда прилетает
TypeError. - Операторы и их результат —
and/orвозвращают операнд, сравнения сцепляются,+=не всегда то же, что+. - bool как подкласс int —
True == 1,True + True == 2и схлопывание ключей1,True,1.0. - Плавающая точка — двоичное представление,
math.iscloseиDecimalдля денег. - Строки — неизменяемость, кодовые точки против графем и квадратичная склейка.
- str, bytes и кодировки — текст против октетов,
encode/decodeи отсутствие неявных преобразований. - Методы строк — все они возвращают новый объект;
split,stripиjoinс их ловушками. - f-строки — поля подстановки, спецификация формата,
!r,=и экранирование{{. - Генераторы коллекций — comprehension против цикла и против ленивого генераторного выражения.
- Сложность операций — цена индекса,
in,insert(0)и почемуdictбыстрееlist.
Частые ошибки и ловушки
| Ошибка | Последствие |
|---|---|
Сравнивать числа и строки через is | На малых целых и литералах случайно работает, а на значениях из ввода или вычислений молча даёт False |
Считать, что b = a копирует коллекцию | Оба имени указывают на один объект — правка через b видна через a; нужен a.copy(), а для вложенных структур copy.deepcopy |
Строить матрицу как [[0] * 3] * 3 | Внешнее умножение размножает ссылку на один список — запись в одну строку меняет все |
Ждать от 0.1 + 0.2 ровно 0.3 | Двоичное представление даёт 0.30000000000000004; сравнивать через math.isclose, деньги считать в Decimal или в копейках |
| Класть изменяемый объект в ключ или менять ключ после вставки | list и dict в ключах дают TypeError, а мутация ключа меняет его хеш — запись остаётся в таблице, но перестаёт находиться |
| Удалять элементы из списка прямо во время итерации по нему | Индексы сдвигаются под итератором, часть элементов пропускается; итерируйтесь по копии или перестраивайте список comprehension |
Проверять вхождение через in по списку в горячем цикле | Список сканируется за O(n) — на десятках тысяч элементов это на четыре порядка медленнее set |
Считать bool отдельным типом, не связанным с числами | bool — подкласс int, поэтому True + True == 2, а {1: 'a', True: 'b'} схлопывается в один ключ |
Значение для собеседований
Это самая частая тема первого технического экрана. Проверяют не заучивание списка типов, а наличие модели. Классический сценарий — короткий фрагмент с алиасингом (b = a, [[0]*3]*3, t[0] += [100]), где надо предсказать вывод и объяснить его через ссылки и изменяемость. Следом идёт is против == на 256 и 257; правильный ответ называет кэш малых целых и добавляет главное — на is для значений полагаться нельзя никогда. Третий обязательный вопрос — почему dict быстрее list на поиске; здесь ждут слова «хеш-таблица», «O(1) в среднем» и «элемент обязан быть хешируемым».
Дальше собеседование расходится по уровням. Junior-набор — list против tuple, срезы, отрицательные индексы, распаковка, zip/enumerate, 0.1 + 0.2. Middle получает str против bytes, представления словаря, сравнение последовательностей, поведение and/or и debugging-задачи вроде KeyError в счётчике слов. Senior-уровень — устройство хеш-таблицы CPython и вопрос-ловушка {True: 'a', 1: 'b', 1.0: 'c'}. Типичная ошибка на всех уровнях одна — ответ формулируется в терминах «переменная содержит значение» вместо «имя ссылается на объект», и дальше кандидат не может объяснить ни один из наблюдаемых эффектов.