В чем разница между списком и строкой в плане изменяемости и поведения по ссылке в Python?
Что проверяют
Понимаете ли вы модель данных Python: переменная это имя, привязанное к объекту, а не ячейка со значением. Следом обычно дают код с изменяемым аргументом по умолчанию или с += и просят предсказать вывод.
Как отвечать
Разведите два вопроса, которые часто смешивают.
Присваивание и передача в функцию работают одинаково для любых типов: копия не создаётся, новое имя указывает на тот же объект. После b = a выражение a is b истинно и для списка, и для строки. Это называют передачей ссылки на объект (call by sharing).
Разница в изменяемости:
list: изменяемый.append,extend, присваивание по индексу и срезу меняют сам объект, поэтому изменение видно через все имена, которые на него указывают, в том числе внутри функции, куда список передали;str: неизменяемый. Методов, меняющих строку на месте, нет:replace,upperи конкатенация возвращают новый объект.s += "x"создаёт новую строку и перепривязывает имяs, остальные имена смотрят на старую;- хэшируемость: строка может быть ключом
dictи элементомset, список нет; +=у списка: вызывает__iadd__, то есть расширяет тот же объект. Аa = a + [4]создаёт новый список. Любимая ловушка на собеседовании.
Практические следствия: def f(items=[]) делит один список между всеми вызовами, поэтому пишут items=None. [[]] * 3 даёт три ссылки на один вложенный список. Копию делают через a.copy() или a[:], но она поверхностная, для вложенных структур нужен copy.deepcopy. Строку в цикле собирают через "".join(parts), а не многократным +=.
Где спотыкаются
Говорят, что при присваивании строки «создаётся копия». Копии нет, просто изменить объект строки невозможно, поэтому поведение похоже на копирование.
Сравнивают строки через is. Иногда это срабатывает из-за интернирования в CPython, но это деталь реализации, сравнивать нужно через ==.
Считают кортеж полностью неизменяемым: в t = ([1],) элемент заменить нельзя, но t[0].append(2) пройдёт, и такой кортеж уже нельзя захэшировать.