tags:

views:

79

answers:

6

Hello.

While reading up the documentation for dict.copy(), it says that it makes a shallow copy of the dictionary. Same goes for the book I am following (Beazley's Python Reference), which says:

The m.copy() method makes a shallow copy of the items contained in a mapping object and places them in a new mapping object.

Consider this:

>>> original = dict(a=1, b=2)
>>> new = original.copy()
>>> new.update({'c': 3})
>>> original
{'a': 1, 'b': 2}
>>> new
{'a': 1, 'c': 3, 'b': 2}

So I assumed this would update the value of original (and add 'c': 3) also since I was doing a shallow copy. Like if you do it for a list:

>>> original = [1, 2, 3]
>>> new = original
>>> new.append(4)
>>> new, original
([1, 2, 3, 4], [1, 2, 3, 4])

This works as expected.

Since both are shallow copies, why is that the dict.copy() doesn't work as I expect it to? Or my understanding of shallow vs deep copying is flawed?

+10  A: 

By "shallow copying" it means the content of the dictionary is not copied by value, but just creating a new reference.

>>> a = {1: [1,2,3]}
>>> b = a.copy()
>>> a, b
({1: [1, 2, 3]}, {1: [1, 2, 3]})
>>> a[1].append(4)
>>> a, b
({1: [1, 2, 3, 4]}, {1: [1, 2, 3, 4]})

In contrast, a deep copy will copy all contents by value.

>>> c = copy.deepcopy(a)
>>> a, c
({1: [1, 2, 3, 4]}, {1: [1, 2, 3, 4]})
>>> a[1].append(5)
>>> a, c
({1: [1, 2, 3, 4, 5]}, {1: [1, 2, 3, 4]})

So:

  1. a = b: Reference assignment, Make a and b points to the same object.

    a ---,
         v
         {1: L}
         ^   |
    b ---'   '----> [1,2,3]
    
  2. a = b.copy(): Shallow copying, a and b will become two isolated objects, but their contents still share the same reference

    a ---> {1: L}
               |             
               >---> [1,2,3]
               |
    b ---> {1: M}
    
  3. a = copy.deepcopy(b): Deep copying, a and b's structure and content become completely isolated.

    a ---> {1: L}
               ‘-----> [1,2,3]
    b ---> {1: M}
               ‘-----> [1,2,3]
    
KennyTM
Kenny, thanks for the awesome explanation.
sukhbir
Though can you please explain why it was not working for my case?
sukhbir
+1 for ASCII art.
Ashish
@Pulp: Because the dictionary structure are isolated. The shallow copies makes the "contents" `1`, `2`, `'a'`, `'b'` shared by both `new` and `original`, but the "structures" `{P:Q, R:S}` of them are no longer linked, i.e., when you mutate `new` to `{P:Q, R:S, T:U}`, the original is not affected.
KennyTM
A: 

The m.copy() method makes a shallow copy of the items contained in a mapping object and places them in a new mapping object.

So the dict does get deepcopied, but the items in it don't. Since the items in the dict are integers, they get 'deep'copied, since there is no such thing as shallow copying small integers.

Edit: Never mind, see below.

Mzialla
This does not make any sense. The very point of deep copy is that *the elements are (deep)copied too*. This is why we talk of deep copy, it works in depth : copy the structure, and the elements, and the elements elements, etc., so that no sharing remain.Copying only one layer is the normal copy operation, that in contrast we name *shallow* copy.
gasche
Well I just tested it out and indeed gasche is right.
sukhbir
Yes, it does make sense.>>> dic1 = {'hello' : 'goodbye'}>>> dic2 = dic1>>> dic1['but'] = 'he said'>>> dic2{'hello': 'goodbye', 'but': 'he said'}
Mzialla
It does not make sense to say that "The dict does get deepcopied, but the items in it don't". It is in direct contradiction with the definition of deep copy (which also copy the items).I also do not understand the point of your code snippet. There is an assignment but no copy at all.
gasche
I misinterpreted your reply, sorry. I stand corrected.
Mzialla
I'm sorry I have been a bit rude in my comments. I find it hard to express my p.o.v. when the disagreement is mostly based on vocabulary/defintions.
gasche
+1  A: 

"new" and "original" are different dicts, that's why you can update just one of them.. The items are shallow-copied, not the dict itself.

Joril
A: 

Contents are shallow copied.

So if the original dict contains a list or another dictionary, modifying one them in the original or its shallow copy will modify them (the list or the dict) in the other.

Ashish
+1  A: 

Take this example:

original = dict(a=1, b=2, c=dict(d=4, e=5))
new = original.copy()

Now let's change a value in the 'shallow' (first) level:

new['a'] = 10
# new = {'a': 10, 'b': 2, 'c': {'d': 4, 'e': 5}}
# original = {'a': 1, 'b': 2, 'c': {'d': 4, 'e': 5}}
# no change in original, since ['a'] is an immutable integer

Now let's change a value one level deeper:

new['c']['d'] = 40
# new = {'a': 10, 'b': 2, 'c': {'d': 40, 'e': 5}}
# original = {'a': 1, 'b': 2, 'c': {'d': 40, 'e': 5}}
# new['c'] points to the same original['d'] mutable dictionary, so it will be changed
eumiro
A: 

It's not a matter of deep copy or shallow copy, none of what you're doing is deep copy.

Here:

>>> new = original 

you're creating a new reference to the the list/dict referenced by original.

while here:

>>> new = original.copy()
>>> # or
>>> new = list(original) # dict(original)

you're creating a new list/dict which is filled with a copy of the references of objects contained in the original container.

Lie Ryan