В Питоне переменные передаются по ссылке или по значению? Есть подводные камни?

Роман

Все завист от того, какой тип объектов ты передаешь в качестве переменной. В Питоне существует два типа объектов

Неизменяемые (immutable)

Неизменяемые объекты передаются по значению. Это значит, что при изменении значения переменной будет создан новый объект. К этому типу относятся:

  • числовые данные (int, float, complex)
  • символьные строки (str)
  • кортежи (tuple) При инициализации переменной незменяемого типа создается объект (например, целое число), этот объект имеет некоторый идентификатор:
>>> a = 10 
>>> id(a)
10914656

оператор = связывает переменную a и объект посредством ссылки. При этом вы не можете изменить сам объект, т.е. когда вы присвоите переменной новое значение, интерпретатор создаст новый объект (если до этого этот объект был создан, то переменная просто получит ссылку), а первоначальный объект удалится из памяти сбощиком мусора, если ссылок на него больше нет.

Изменяемые (mutable)

Изменяемые объекты передаются по ссылке. Это значит, что при изменении значения переменной объект будет изменен. К этому типу относятся:

  • списки (list)
  • множества (set)
  • словари (dict)

Подводные камни

Создадим список a, установим для переменной b ссылку на a, прибавим к b элемент списка и выведем их значения и идентификаторы на экран:

>>> a = [1, 2]
>>> b = a
>>> b.append(3)
>>> print(a, b)
[1, 2, 3] [1, 2, 3]
>>> print(id(a), id(b))
139748057891656 139748057891656

Как мы видим, переменные имеют одинаковые id и элементы списка. Если ты не знаешь об этой особенности изменяемых объетов, то такое поведение программы для тебя становится полной неожиданностью и может привести к ошибке в работе программы. Таким же образом с помощью ссылки на изменяемый объект, переменная передается в функцию:

>>> def add_value(a):
...  a.append(3)
>>> b = [1, 2]
>>> add_value(b)
>>> print(b)
[1, 2, 3]

Даже возвращая None, функция изменила список b, чего бы нам не хотелось.

Что с этим можно сделать

Для того, чтобы передать в функцию изменяемую переменную как значение, нужно сделать копию изменяемого элемента. Создадим копию списка:

новый_лист = старый_лист[:]

Тоже самое можем сделать вот так:

новый_лист = list(старый_лист)

Переменная новый_лист ссылается на новый объект:

>>> id(старый_лист), id(новый_лист)
(139748050279112, 139748057891656)

Это дает нам возможность изменять оба объекта независимо друг от друга.

Что почитать

Попробуйте бесплатные уроки по Python

Получите крутое код-ревью от практикующих программистов с разбором ошибок и рекомендациями, на что обратить внимание — бесплатно.

Переходите на страницу учебных модулей «Девмана» и выбирайте тему.