Копирование словаря с вложенным списком и кортежем

Основной вопрос: Почему в копии словаря 'new_var' значение словаря 'var' в индексе '[0]', 'list', меняется, а в '[1]', 'tuple', нет? Ведь оба параметра в данном примере идут как вложения и что бы они не менялись, нужно использовать 'deepcopy'. В чем моя ошибка?

Весь код:

var = {'list': [[1, 2], ['a', 'b']], 'tuple': ('foo', 'bar', )}
new_var = var.copy()
var.update({'str': 'string'})
var['list'].append ([True, False])
var['tuple'] += ('baz', )

Идем пошагово.

Есть словарь:

var = {'list': [[1, 2], ['a', 'b']], 'tuple': ('foo', 'bar', )}

Создаем его копию (не глубокую):

new_var = var.copy()

Делаем 'var.update'

var.update({'str': 'string'})

Получаем 'var' и 'new_var':

{'list': [[1, 2], ['a', 'b']], 'tuple': ('foo', 'bar'), 'str': 'string'}
{'list': [[1, 2], ['a', 'b']], 'tuple': ('foo', 'bar')}

'var' обновился со значением '{'str': 'string'}', а 'new_var' остался без изменений, т.к. это копия.

Далее делаем '.append':

var['list'].append ([True, False])

Получаем 'var' и 'new_var':

{'list': [[1, 2], ['a', 'b'], [True, False]], 'tuple': ('foo', 'bar'), 'str': 'string'}
{'list': [[1, 2], ['a', 'b'], [True, False]], 'tuple': ('foo', 'bar')}

Первый вопрос: Почему значение листа '[True, False]' было перенесено в копию 'new_var'? (Полагаю потому, что это вложение в виде ссылки)

Далее добавляем пункт в 'tuple':

var['tuple'] += ('baz', )

Получаем 'var' и 'new_var':

{'list': [[1, 2], ['a', 'b'], [True, False]], 'tuple': ('foo', 'bar', 'baz'), 'str': 'string'}
{'list': [[1, 2], ['a', 'b'], [True, False]], 'tuple': ('foo', 'bar')}

Второй вопрос: Почему в этом случае 'baz' добавилось только в словарь 'var', но не в его копию 'new_var', как это было в прошлом пункте?

P.S. Вероятно, мой вопрос глупый, я только учусь.


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

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

Разница в том, что tuple в Python является неизменяемым (immutable) объектом . Поэтому для кортежей не существует метода .append() и вам пришлось заменить значение по ключу "tuple" ссылкой на новый кортеж.

Ваш пример:

Out[321]:
{'list': [[1, 2], ['a', 'b'], [True, False]],
 'tuple': ('foo', 'bar'),
 'str': 'string'}

In [322]: id(var["tuple"])
Out[322]: 140364687889408

In [323]: var['tuple'] += ('baz', )

In [324]: id(var["tuple"])
Out[324]: 140364687888704  # <--- обратите внимание на изменившийся `id` !

In [325]: var
Out[325]:
{'list': [[1, 2], ['a', 'b'], [True, False]],
 'tuple': ('foo', 'bar', 'baz'),
 'str': 'string'}

In [326]: id(new_var["tuple"])
Out[326]: 140364687889408  # <-- new_var["tuple"] все еще указывает на кортеж __до__ изменения

var['tuple'] += ('baz', ) эквивалентно:

var['tuple'] = var['tuple'] + ('baz', )

А var['tuple'] + ('baz', ) создает новый кортеж с результатом сложения.

→ Ссылка