Изменение значения словаря при изменении переменной

Только начинаю изучать python, столкнулся с неочевидным для меня феноменом.

При заполнении значения словаря переменной-словарём оказывается, что после изменения этой переменной меняется и значение в словаре.

var_dict_value = {'one' : 1, 'two' : 2}

var_dict = {}
var_dict ['first'] = var_dict_value

print(var_dict) 
# {'first': {'one': 1, 'two': 2}}


print('chg var_dict_value to 7777')
var_dict_value['one'] = 7777

print(var_dict)
# {'first': {'one': 7777, 'two': 2}}

Из этого я могу сделать вывод, что ссылаясь на переменную мы работаем не с её значением, а с ней как с указателем на значение (я где-то слышал умные слова про то, что в python всё есть объект, но мне это пока ни о чём не говорит).

Но аналогичная ситуация с обычными переменными (не словарями) даёт иной эффект - значение переменной не меняется после изменения переменной, участвующей в её определении

a = 5
b = a

print(f'a = {a}, b = {b}')
# a = 5, b = 5

a = 10
print(f'a = {a}, b = {b}')
# a = 10, b = 5

Отсюда вопросы:

  1. В чём принципиальная разница двух ситуаций? почему в первом кейсе значение (в словаре) меняется после изменения переменной, а во втором - нет
  2. Как "законстантить" значение в случае с словарём? то есть, как добиться того, чтобы при изменении var_dict_value не менялись бы данные в var_dict ?

Ответы (1 шт):

Автор решения: Dmitry

попробую объснить вот на этом примере

a = 5
b = a

print(f'a = {a}, b = {b}')
# a = 5, b = 5

a = 10
print(f'a = {a}, b = {b}')
# a = 10, b = 5

в первых двух строках вы определяете две переменные, которые ссылаются на один объект.

Далее, вы изменяете переменную a, что буквально значит, что вы создаете новый объект в памяти и в переменную a кладете ссылку на него. Следовательно, ссылка на интовое значение 5 после строки a = 10, хранится только в переменной b. И это вы видите когда выводите значение в последней строке.

В этом примере

var_dict_value = {'one' : 1, 'two' : 2}

var_dict = {}
var_dict ['first'] = var_dict_value

print(var_dict) 
# {'first': {'one': 1, 'two': 2}}


print('chg var_dict_value to 7777')
var_dict_value['one'] = 7777

print(var_dict)
# {'first': {'one': 7777, 'two': 2}}

Сущности var_dict ['first'] и var_dict_value ссылаются на один и тот же объект в памяти. Если вы поменяете любой из них, то изменения вы увидите и в другом

Пример номер раз

var_dict ['first']["two"] = 233
print(var_dict_value)
#out
# {'one': 1, 'two': 233}

Пример номер два

var_dict_value["one"] = "blabla"
print(var_dict ['first'])
#out
# {'one': 'blabla', 'two': 233}

Различия здесь таковы, что словарь - это изменяемый тип данных, а вот int - не изменяемый тип данных.

Если зайти поглубже, то можно понять, что переменные под ключами словаря тоже не изменяемые и при переопределении так же создается новый объект в памяти и ссылка на него, которая будет лежать под ключом словаря =) а старые объекты будут уничтожены (интовые значения 1 и 2) после последнего изменения, поскольку нет ни одной ссылки, которые будут вести на эти объекты

Чтобы избежать изменений описанных выше необходимо использовать методы copy() и deepcopy(). Различия можно прочесть в документации.

Коротко

copy() - создает новый объект, а затем (насколько это возможно, стоит проверять "насколько") вставляет в него ссылки на объекты, найденные в оригинале.

deepcopy() - создает новый объект, а затем рекурсивно вставляет в него копии объектов, найденных в оригинале.

PS: изменяемые объекты, которые доступны по ссылке могут быть изменены и не существует механизма предотвратить это. Это ответственность программиста использовать верные типы данных и следить за правильной их обработкой

→ Ссылка