Methods to clone a python list

Python list are one of the most used data structure. An improper cloning of a list has burnt more midnights’ oil than creation & manipulation of the list has taken. Cloning isn’t difficult but plain different. Of course, I am not considering the most obvious method iterate through the list and create a new list 🙂

Basic understanding of how the list object is implemented within will give an edge on how to use the list esp from cloning perspective.  Most important point in lists is it is mutable. A list object contains a list of pointers to PyObjects. A PyObject contains a pointer to the corresponding type object. In the words that pleases the C-language enthusiasts, list is a pointer to an array of pointers!
visual description of python lists
list is a pointer to an array of pointers
With this understanding, let us see the various methods of cloning a list. Let us establish the base, i.e., define the original list.
list_1 = [1,2,3]
list_2 = [4,5,6]
original = [ list_1, list_2 ]
Visual representation of original list
Original list

Clone by assignment

new = original

The new list “new” points to the original list. So, any changes to the original list will be reflected in the new list.

visual description of cloning through assignment
clone a list through assignment
Let us try out an example:
list_1 = [1,2,3]
list_2 = [4,5,6]
original = [ list_1, list_2 ]

new = original

print 'original: ', original
print 'new: ', new

list_2.append(7)
list_3 = [8, 9]
original.append(list_3)

print '********************************'
print 'original: ', original
print 'new: ', new1
The output of the above python script confirms our understanding:
$ python clone-by-assign.py
original:  [[1, 2, 3], [4, 5, 6]]
new:  [[1, 2, 3], [4, 5, 6]]
********************************
original:  [[1, 2, 3], [4, 5, 6, 7], [8, 9]]
new:  [[1, 2, 3], [4, 5, 6, 7], [8, 9]]
As it can be seen, clone by assignment results in a list that is tightly coupled with the original list. So any updates to the original list is seen on the cloned list.

 

Clone by slice

new = original[:]

The new list “new” creates a copy of original list but shares the content with the original list. So, any changes to the original list will NOT affect the new list but changes to the content will alter the new2 as well.

visual representation of cloning by slicing
create clone using slicing of the original list
Let us try out an example:
list_1 = [1,2,3]
list_2 = [4,5,6]
original = [ list_1, list_2 ]

new = original[:]

print 'original: ', original
print 'new: ', new

list_2.append(7)
list_3 = [8, 9]
original.append(list_3)

print '********************************'
print 'original: ', original
print 'new: ', new
The output of the above python script confirms our understanding:
$ python clone-by-slice.py
original:  [[1, 2, 3], [4, 5, 6]]
new:  [[1, 2, 3], [4, 5, 6]]
********************************
original:  [[1, 2, 3], [4, 5, 6, 7], [8, 9]]
new:  [[1, 2, 3], [4, 5, 6, 7]]
As it can be seen, clone by slice results in a list that is NOT tightly coupled with the original list but tightly coupled with the content of the original list. So, updates to the original list does not affect the cloned list but updates to the content of the original list affect the cloned list.

 

Clone by copy constructor

new = list(original)

This is exactly same as the clone by slice.

$ python clone-by-copy-constructor.py
original:  [[1, 2, 3], [4, 5, 6]]
new:  [[1, 2, 3], [4, 5, 6]]
********************************
original:  [[1, 2, 3], [4, 5, 6, 7], [8, 9]]
new:  [[1, 2, 3], [4, 5, 6, 7]]

 

Clone by copy module

There is a copy module that can be imported and gives 2 different methods to copy:

    1. copy()
    2. deepcopy()
Cloning by copy()

This is exactly same as slice & copy-constructor.

import copy
new = copy.copy(original)

This is exactly same as the clone by slice, returns a shallow copy of the original.

$ python clone-by-copy.py
original:  [[1, 2, 3], [4, 5, 6]]
new:  [[1, 2, 3], [4, 5, 6]]
********************************
original:  [[1, 2, 3], [4, 5, 6, 7], [8, 9]]
new:  [[1, 2, 3], [4, 5, 6, 7]]
Cloning by deepcopy()

This is the ultimate copy that we might require. This creates an entirely new list, which has no bindings whatsoever with the original list.

Visual representation of deepcopy effect
create clone using deepcopy function from the copy module
list_1 = [1,2,3]
list_2 = [4,5,6]
original = [ list_1, list_2 ]

new = copy.deepcopy(original)

print 'original: ', original
print 'new: ', new

list_2.append(7)
list_3 = [8, 9]
original.append(list_3)

print '********************************'
print 'original: ', original
print 'new: ', new
The output of the above python script confirms our understanding:
$ python clone-by-deepcopy.py
original:  [[1, 2, 3], [4, 5, 6]]
new:  [[1, 2, 3], [4, 5, 6]]
********************************
original:  [[1, 2, 3], [4, 5, 6, 7], [8, 9]]
new:  [[1, 2, 3], [4, 5, 6]]

 

So, depending on the requirement, the appropriate clone method should be chosen.

The blogs/forums I referred to:
  •  Laurent Luce’s Blog: Beautiful explanation of python lists. Do check out the comments sections for furthering your knowledge with time complexity et al 🙂
  • Stackoverflow discussion: Some fabulous responses including speed comparison.

No Comments

Post a Comment

%d bloggers like this: