Изменение значения словаря при изменении переменной
Только начинаю изучать 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
Отсюда вопросы:
- В чём принципиальная разница двух ситуаций? почему в первом кейсе значение (в словаре) меняется после изменения переменной, а во втором - нет
- Как "законстантить" значение в случае с словарём? то есть, как добиться того, чтобы при изменении var_dict_value не менялись бы данные в var_dict ?
Ответы (1 шт):
попробую объснить вот на этом примере
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: изменяемые объекты, которые доступны по ссылке могут быть изменены и не существует механизма предотвратить это. Это ответственность программиста использовать верные типы данных и следить за правильной их обработкой