В питоне всё, что можно положить в переменную, является объектом. А сама переменная является ссылкой на этот объект.

a = 1
a = 2
b = a

Объекты «живут» в своём мире независимо от переменных, а переменные — один из способов обратиться извне к миру объектов. Так в примере выше объект Целое-число-1 продолжает жить даже после того, как мы поменяли переменную a.

Объекты в питоне бывают двух значительно отличающихся сортов: изменяемые (mutable) и неизменяемые (immutable). Неизменяемыми являются целые и действительные числа (int, float), строки (str), последовательности байтов (бинарные данные, bytes), а также кортежи, все элементы которых неизменяемы (tuple). Напротив, списки (list), словари (dict) и множества (set) являются изменяемыми. Что всё это значит? Неизменяемые объекты обладают замечательным свойством: они не могут измениться в результате работы программы. Если некоторая переменная «смотрит» на неизменяемый объект, то можно быть уверенным, что её значение не поменяется, если только ей не присвоить ссылку на другой объект. Например, в результате выполнения кода a = 1 в переменной a хранится ссылка на объект Целое-число-1, и что бы не происходило с другими переменными, a будет всегда равно 1.

Изменяемые объекты не обладают таким постоянством. Они скорее напоминают контейнер для хранения: контейнер остаётся на месте, а вот содержимое может сильно измениться. Что всё это для нас значит? Ну, например, если передать список в «чужую» функцию, то его могут испортить до неузнаваемости:

def list_destroyer(l):
    l[0] = 'ha'
    l[1] = 'hi'
    l[2] = 'ho'

a = [1, 2, 3]
print(a)  # -> [1, 2, 3]
list_destroyer(a)
print(a)  # -> ['ha', 'hi', 'ho']
Здесь можно поиграться с этим примером и посмотреть на то, что происходит с ссылками:

Обратите внимание, при передаче списка в функцию можно модифицировать элементы списка при помощи присваиваний A[i] = ..., методов pop, append, insert и т.д. Это не является модификацией списка, как целого, т.е. имя A по-прежнему указывает на тот же список, при этом содержимое списка меняется.

Не всегда такое поведение является ожидаемым:

a = [1, 2, 3]
b = a
b.insert(0, 'ops')
b.pop()
print(b)  # -> ['ops', 1, 2]
print(a)  # -> ['ops', 1, 2]

Присваивание b = a не создает новый список, а всего лишь делает b ссылкой на уже существующий список. Для того, чтобы «сохранить» старый список, нужно создавать его копию:

a = [1, 2, 3]
b = a.copy()
b.insert(0, 'ops')
b.pop()
print(b)  # -> ['ops', 1, 2]
print(a)  # -> [1, 2, 3]
Последнее изменение: Суббота, 15 Август 2020, 02:35