ChatGPT解决这个技术问题 Extra ChatGPT

Understanding dict.copy() - shallow or deep?

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?

Quaint that they don't explain "shallow". Insider knowledge, wink. Just the dict and keys are a copy while nested dicts inside that first level are references, cannot be deleted in a loop for example. Thus Python's dict.copy() in that case is neither useful nor intuitive. Thanks for your question.

k
kennytm

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.

>>> import copy
>>> 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:

b = a: Reference assignment, Make a and b points to the same object. b = a.copy(): Shallow copying, a and b will become two isolated objects, but their contents still share the same reference b = copy.deepcopy(a): Deep copying, a and b's structure and content become completely isolated.


Nice answer, but you might consider correcting the grammatical error in your first sentence. And there's no reason to not use L again in b. Doing so would simplify the example.
@kennytm: What is the difference between the first two examples, in fact? You get there the same result, but slightly different inner implementation, but for what does it matter?
@JavaSa a[1] = {} will affect b in the first example, and won't affect b in the second example.
Great explanation,... really saved my day! Thanks...Can this same be applied tp the list, str, and other datatypes of python?
@Sheldore dic_a and dic_b are referencing the same dictionary when you dic_b = dic_a. The strings in the dictionary are immutable but that's irrelevant, because dic_b['A'] = 'Adam' mutates the dictionary itself.
e
eumiro

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

no change in original, since ['a'] is an immutable integer This. It actually answers the question asked.
L
Lie Ryan

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.


True, as far as it goes... but it doesn't go far enough to answer the OP question about shallow vs. deep.
V
Vkreddy

Adding to kennytm's answer. When you do a shallow copy parent.copy() a new dictionary is created with same keys,but the values are not copied they are referenced.If you add a new value to parent_copy it won't effect parent because parent_copy is a new dictionary not reference.

parent = {1: [1,2,3]}
parent_copy = parent.copy()
parent_reference = parent

print id(parent),id(parent_copy),id(parent_reference)
#140690938288400 140690938290536 140690938288400

print id(parent[1]),id(parent_copy[1]),id(parent_reference[1])
#140690938137128 140690938137128 140690938137128

parent_copy[1].append(4)
parent_copy[2] = ['new']

print parent, parent_copy, parent_reference
#{1: [1, 2, 3, 4]} {1: [1, 2, 3, 4], 2: ['new']} {1: [1, 2, 3, 4]}

The hash(id) value of parent[1], parent_copy[1] are identical which implies [1,2,3] of parent[1] and parent_copy[1] stored at id 140690938288400.

But hash of parent and parent_copy are different which implies They are different dictionaries and parent_copy is a new dictionary having values reference to values of parent


J
Joril

"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.


J
Jungle Hunter

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.


S
Stephen Rauch

In your second part, you should use new = original.copy()

.copy and = are different things.


关注公众号,不定期副业成功案例分享
Follow WeChat

Success story sharing

Want to stay one step ahead of the latest teleworks?

Subscribe Now