Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do changes to a nested dict inside dict2 affect dict1? [duplicate]

I don't understand these cases:

content = {'a': {'v': 1}, 'b': {'v': 2}}
d1 = {'k1': {}}
d2 = {'k2': {}}
d1['k1'].update(content)
print(d1)
content['a']['v'] = 3
content['b']['v'] = 4
d2['k2'].update(content)
print(d2)
print(d1)
>>> {'k1': {'a': {'v': 1}, 'b': {'v': 2}}}
>>> {'k2': {'a': {'v': 3}, 'b': {'v': 4}}}
>>> {'k1': {'a': {'v': 3}, 'b': {'v': 4}}}

In the case above the content of d1 is changed after the variable content is updated.

content = {'a': 1, 'b': 2}
d1 = {'k1': {}}
d2 = {'k2': {}}
d1['k1'].update(content)
print(d1)
content['a'] = 3
content['b'] = 4
d2['k2'].update(content)
print(d2)
print(d1)
>>> {'k1': {'a': 1, 'b': 2}}
>>> {'k2': {'a': 3, 'b': 4}}
>>> {'k1': {'a': 1, 'b': 2}} 

However in this case d1 is not altered even if the variable content was changed. I don't understand why... any idea?

like image 839
Marc Avatar asked May 21 '18 09:05

Marc


People also ask

How can you create a copy of a dictionary modifying the copy should not change the original )?

Use copy() This is a built-in Python function that can be used to create a shallow copy of a dictionary. This function takes no arguments and returns a shallow copy of the dictionary. When a change is made to the shallow copy, the original dictionary will remain unchanged.

Which statement creates a duplicate copy of a dictionary?

The dict. copy() method returns a shallow copy of the dictionary. The dictionary can also be copied using the = operator, which points to the same object as the original. So if any change is made in the copied dictionary will also reflect in the original dictionary.

What are the ways to copy a dictionary d2 to d1?

Copy a dictionary with a for loop To copy a dictionary it is also possible to use a for loop: >>> d1 = {'a':1,'b':2} >>> d2 = {} >>> for key in d1: ... d2[key] = d1[key] ...


2 Answers

see shallow vs deep copy.

The copy here is a shallow copy so the first level entries are copies but the nested structures are references.

  • A shallow copy constructs a new compound object and then (to the extent possible) inserts references into it to the objects found in the original.
  • A deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found in the
    original.
like image 157
perreal Avatar answered Sep 25 '22 03:09

perreal


The key difference between your two snippets is that content['a']['v'] = 3 is a completely different operation than content['a'] = 3. In the first case, you're modifying the inner dictionary by changing its v key. In the latter case, you're replacing the value in the dictionary without modifying it.

It's confusing when everything's a dictionary, so let's replace the dictionaries with variables and instances of a class:

class Person:
    def __init__(self, name):
        self.name = name

# these two variables are represent your `content` dict
a = Person('Andy')  # this variable represents `{'v': 1}`
b = Person('Belle')  # this variable represents `{'v': 2}`

# the equivalent of `d1['k1'].update(content)` is a simple assignment
k1_a = a

# and the equivalent of `content['a']['v'] = 3` is changing a's name
a.name = 'Aaron'

# because k1_a and a are the same Person instance, this is reflected in k1_a:
print(k1_a.name)  # output: Aaron

The key points to note here are that

  1. k1_a = a doesn't make a copy of the Person; similar to how d1['k1'].update(content) doesn't make a copy of the {'v': 1} dict.
  2. a.name = 'Aaron' modifies the Person; similar to how content['a']['v'] = 3 modifies the inner dict.

The equivalent of your 2nd snippet looks like this:

a = 'Andy'
b = 'Belle'

k1_a = a

a = 'Aaron'

print(k1_a)  # output: Andy

This time, no object is ever modified. All we're doing is overwriting the value of the a variable, exactly how content['a'] = 3 overwrites the value of the a key in your dict.


If you don't want the changes in the inner dicts to be reflected in other dicts, you have to copy them with copy.deepcopy:

import copy

content = {'a': {'v': 1}, 'b': {'v': 2}}
d1 = {'k1': {}}
d2 = {'k2': {}}
d1['k1'].update(copy.deepcopy(content))
print(d1)
content['a']['v'] = 3
content['b']['v'] = 4
d2['k2'].update(copy.deepcopy(content))
print(d2)
print(d1)

# output:
# {'k1': {'a': {'v': 1}, 'b': {'v': 2}}}
# {'k2': {'a': {'v': 3}, 'b': {'v': 4}}}
# {'k1': {'a': {'v': 1}, 'b': {'v': 2}}}
like image 24
Aran-Fey Avatar answered Sep 24 '22 03:09

Aran-Fey