TheLazy
TheLazy

Reputation: 263

Why does shallow copy behaves as deep copy for a simple list

I was going understanding shallow copy and deep copy concepts in python. I observe most of the posts/blogs/SO answer explain these concepts are using a nested lists.

import copy
lst = [[1,2,3],[4,5,6]]
b = copy.copy(lst)
c = copy.deepcopy(lst)

# Shallow copy demo
b[0][0] = 9
print(b)
# >>> [[9, 2, 3], [4, 5, 6]]
print(lst)    
# >>> [[9, 2, 3], [4, 5, 6]]

# Deepcopy demo
c[0][0] = 10
print(c)
# >>> [[10, 2, 3], [4, 5, 6]] 
print(lst)
# >>> [[9, 2, 3], [4, 5, 6]]

I understood the shallow and deep copy concept with the above simple example. But when I implement the concept, on a simple list (one-dimensional list), the observation is shallow copy behaves as deep copy.

import copy
lst = [1,2,3]
b = copy.copy(lst)
c = copy.deepcopy(lst)

# Shallow copy demo
b[0] = 0
print(b)
# >>> [0, 2, 3]
print(lst)
# >>> [1,2,3]

# Deepcopy demo
c[0] = 9
print(c)
# >>> [9,2,3]
print(lst)
# >>> [1,2,3]

This shows that copy.copy(lst) behaves different and does deep copy instead of shallow copy.

I would like to understand, why the behavior of copy.copy() is different for nested list and simple list. Also if i have to get shallow copy working for simple list, how can i achieve it?.

Upvotes: 3

Views: 1047

Answers (1)

Danirod
Danirod

Reputation: 56

The results that you're getting are not directly related with the "level of depth", the most important thing to keep in mind here is the concept of mutabiliy.

List are mutable, meanwhile numeric values are not. That means that you can add or modify items on a list, but those operations doesn't create or destroy the list, they only change it. You can verify that using the built-in function id(), which gives you the memory address of a variable:

lst = [1, 2, 3]
print(id(lst)) # the number printed by this...
lst.append(4)
lst[1] = 0
print(id(lst)) # should be the same printed by this one. That tells us that 
               # the variable 'lst' keeps referecing the same object, although
               # the object have changed in form (mutated)

Numbers are totally different, and it makes sense, since a numeric type variable can only store a single numeric value:

a = 5
print(id(a)) # the number printed by this...
a = 6
print(id(a)) # should be different than this one, meaning that a new numeric 
             # value were created and stored in a different memory address

On the line

b[0][0] = 9

of your first example, the list at b[0] is being manipulated, but it remains being the same object, and since b[0] is nothing more than a reference to the same list at lst[0] (because b is a shallow copy), when we print lst we will see that it changed too.

On your implementation, when you assign b[0] = 0, python is creating the value 0, storing it on a new memory location, and overriding the reference that b[0] had to the same value as lst[0] (cause thats the natural behavior of numeric types).

As is said, this doesn't have to be with the level of nesting of compound data structures, since some of the are inmutable (as for example the tuple) and the same that happened on your implementation would happen with this inmutable data structures.

You can read some more about the id() built-in function here, and more about mutable and inmutable types here

Hope this answer helps you!

Upvotes: 3

Related Questions