Изменяемые и неизменяемые типы в Python
Список типов помнят все, а спрашивают про следствия: изменяемый аргумент по умолчанию и кортеж со списком внутри. Разбираем, что проверяет вопрос.
Список изменяемых типов запоминается за минуту, и на нём вопрос не заканчивается. Дальше идут следствия — аргумент по умолчанию, который помнит прошлый вызов, кортеж, содержимое которого меняется, и ключ словаря, который вдруг не подходит. Все три выводятся из одного правила, и ниже разобрано, из какого.
Всё, что сказано о поведении, относится к Python 3.
Три свойства объекта
Модель данных языка начинается с утверждения, которое стоит взять за основу ответа: у каждого объекта есть идентичность, тип и значение. Идентичность не меняется никогда — документация предлагает думать о ней как об адресе объекта в памяти. Тип тоже не меняется. Меняться может только значение, и то не всегда.
Ключевая строка звучит дальше и снимает половину вопросов темы: изменяемость объекта определяется его типом. Числа, строки и кортежи неизменяемы; словари и списки изменяемы. Это не свойство конкретного объекта и не результат того, как с ним обращались, — это свойство типа, и потому предсказуемо заранее.
Отсюда выводится и различие между is и ==, о котором спрашивают рядом: is сравнивает идентичность — тот же самый объект или нет; == сравнивает значения. Для неизменяемых объектов разница часто незаметна, и именно поэтому её проверяют на изменяемых.
Аргумент по умолчанию вычисляется один раз
Инженер, ведущий открытые тестовые интервью с джуниорами, называет этот пример первым в наборе ловушек по теме — и он же самый показательный, потому что «странность» здесь ровно нулевая.
Правило записано в справочнике языка прямо: значения параметров по умолчанию вычисляются слева направо при выполнении определения функции. То есть выражение вычисляется один раз, в момент определения, и одно и то же заранее вычисленное значение используется при каждом вызове.
Дальше всё следует само. Если значение по умолчанию — изменяемый объект, например список, и функция его изменяет, скажем добавляет элемент, то изменяется само значение по умолчанию. Документация добавляет: обычно это не то, что имелось в виду. При втором вызове функция получит список, в котором уже лежит результат первого.
Обходной путь тоже предписан документацией, а не придуман сообществом: значением по умолчанию ставят None и явно проверяют его в теле, создавая новый список внутри. Тогда каждый вызов получает свой объект.
Стоит отметить связь, которая делает правило проще для запоминания: это тот же момент, в который вычисляются выражения декораторов, — определение функции, а не её вызов. Одно правило, два разных вопроса на собеседовании.
Неизменяемый контейнер с изменяемым содержимым
Здесь заканчивается учебное изложение — тем, что список типов уже назван, — и начинается случай, который в него не укладывается. Кортеж неизменяем, но если внутри него лежит список, содержимое кортежа наблюдаемо меняется — и это не противоречие.
Документация разбирает случай прямо и формулирует вывод, который стоит уметь воспроизвести: значение неизменяемого контейнера, содержащего ссылку на изменяемый объект, может измениться, когда изменится значение последнего; однако контейнер по-прежнему считается неизменяемым, потому что набор объектов, которые он содержит, изменить нельзя. И следом: неизменяемость — не строго то же самое, что неизменяемое значение, это тоньше.
Разгадка в том, что именно хранит контейнер. Кортеж хранит ссылки, и неизменяемость означает, что нельзя заменить одну ссылку на другую. То, куда эти ссылки ведут, кортежу не подчиняется.
Тот же механизм объясняет ещё два вопроса, которые обычно идут рядом:
- Умножение списка. Выражение вида
[[]] * 3создаёт не три независимых списка, а три ссылки на один и тот же объект. Изменение через любую из них видно через все. +=против=. Для спискаa += [3]изменяет объект на месте, аa = a + [3]создаёт новый объект и связывает имя с ним. Разница незаметна, пока на прежний объект не смотрит кто-то ещё — например, другое имя или элемент кортежа.
Что можно положить в ключ словаря
Последний шаг ничего нового не вводит — он применяет уже сказанное. Ключом словаря может быть только хешируемый объект: словарь использует значение хеша внутри, и оно должно не меняться в течение всей жизни объекта.
Правило для контейнеров словарь языка формулирует одной строкой, и она содержит весь ответ: большинство неизменяемых встроенных объектов хешируемы; изменяемые контейнеры — списки, словари — не хешируемы; а неизменяемые контейнеры вроде кортежей и frozenset хешируемы только в том случае, если хешируемы их элементы.
Отсюда ответ на вопрос про кортеж со списком внутри выводится в одну строку, без примера и без запоминания: кортеж неизменяем, но его элемент — список, а список не хешируем; значит, и кортеж не хешируем, и ключом словаря быть не может.
Полезно назвать и причину, по которой правило именно такое, — она возвращает к предыдущей секции. Если бы такой кортеж был ключом, изменение списка внутри поменяло бы значение хеша, и запись перестала бы находиться там, где её положили. Требование постоянства хеша — не формальность, а условие работоспособности словаря.

Backend-разработчик Python
Учебный маршрут backend-разработчика на Python: язык и среда выполнения CPython, конкурентность, веб-фреймворки и API, данные и ORM, безопасность, продакшн-инженерия и архитектура.

Backend Developer Python
Глубокое понимание Python и его среды выполнения для backend-разработки. Курс разбирает не синтаксис, а семантику языка и поведение CPython: объектную модель и работу со ссылками, модель данных и протоколы, конкурентность на asyncio и следствия глобальной блокировки интерпретатора, прикладной веб-слой (FastAPI, Django, Flask), работу с данными через ORM и продакшн-инженерию. Цель — научиться видеть стоимость абстракций и поведение кода под нагрузкой, а не просто заставлять его работать.
Готовьтесь не вслепую
Возьмите маршрут по своей роли и отрабатывайте ответы на вопросы с AI-разбором.