Как реализуется изменение значения переменной в Python
Что проверяют
Понимаете ли вы модель «имя ссылается на объект», на которой держится весь Python. Вопрос звучит примитивно, но проверяет разницу между присваиванием и изменением объекта. Следом обычно дают код со списком, переданным в функцию, и просят предсказать вывод.
Как отвечать
Переменная в Python это имя, привязанное к объекту. Присваивание не меняет объект, а перепривязывает имя:
- присваивание: после
x = 10иx = 20имяxуказывает на другой объект. Старый объект никто не менял, это видно поid(x); - неизменяемые типы:
int,str,tuple,frozenset. Любая «модификация» создаёт новый объект, поэтомуx += 1для числа означает новое число и перепривязку; - изменяемые типы:
list,dict,set. Метод меняет сам объект, и изменение видят все имена, которые на него ссылаются. Для спискаa += [1]вызывает__iadd__и меняет список на месте, аa = a + [1]создаёт новый; - передача в функцию: передаётся ссылка на объект (call by sharing). Мутация аргумента видна снаружи, перепривязка параметра внутри функции нет;
- области видимости: присваивание внутри функции делает имя локальным. Чтобы перепривязать внешнее имя, нужны
globalилиnonlocal, иначе чтение до присваивания дастUnboundLocalError.
Хорошо добавить копирование: copy.copy копирует только верхний уровень, вложенные списки остаются общими, полную копию даёт copy.deepcopy. И известный пример: для t = ([1],) строка t[0] += [2] бросает TypeError, но список внутри кортежа к этому моменту уже изменён.
Где спотыкаются
Говорят «в Python передача по ссылке» или «по значению». Оба ответа неточны: lst = [] внутри функции не изменит список вызывающего кода, а lst.append(1) изменит.
Используют изменяемое значение по умолчанию: def f(items=[]). Список создаётся один раз при определении функции и копит данные между вызовами, правильно писать items=None.
Сравнивают числа и строки через is. Кэш маленьких целых от -5 до 256 это деталь CPython, и на числах, вычисленных во время работы, is вернёт False при равных значениях.