How to Clone a List in Python

How to Clone a List in Python Featured Image

Hello again! Welcome to the sixth installment of the Python Bytes series. Today, we’re going to learn how to clone a list in Python.

Table of Contents

Problem Introduction

Imagine that we have a list:

my_list = [27, 13, -11, 60, 39, 15]

And, we want to create a duplicate of this list, so we can modify their contents independently:

my_list = [27, 13, -11, 60, 39, 15]
my_duplicate_list = [27, 13, -11, 60, 39, 15]

my_list.append(17)
print(my_list)  # prints [27, 13, -11, 60, 39, 15, 17]
print(my_duplicate_list)  # prints [27, 13, -11, 60, 39, 15]

How would we go about doing that?

Background

Before we dive in, there are a couple of topics we should probably cover first. After all, cloning can be a bit counterintuitive, so it’s important that we take a step back to discuss duplicate references and deep copies.

Duplicate References

If you’ve come to this article, it’s probably because you’ve tried cloning a list by hand, and you’ve run into some problems. For instance:

my_list = [27, 13, -11, 60, 39, 15]
my_duplicate_list = my_list  # done

Unfortunately, this doesn’t really do the job. After all, we haven’t actually duplicated the list. We’ve simply stored the reference to it in another variable. If we try to modify our duplicate list, we’ll modify the original as well. Take a look:

my_duplicate_list.append(7)
print(my_duplicate_list)  # prints [27, 13, -11, 60, 39, 15, 7]
print(my_list)  # prints [27, 13, -11, 60, 39, 15, 7]

So, clearly that’s not what we want. Instead of duplicating our list, we’ve simply created an alias – another variable that refers to the same list.

Deep Copies

In addition, we should probably cover something known as deep copying. Let’s say we have a list that contains lists:

my_list = [[27], [13], [-11], [60], [39], [15]]

If we decide to perform a simple copy on this list, we’ll end up with some strange behavior:

my_list_copy = copy(my_list)  # a placeholder copy function
print(my_list_copy)  # prints [[27], [13], [-11], [60], [39], [15]]  # prints as expected

Alright, so no problems yet. In fact, we can even append information to the new list without any problems:

my_list_copy.append([17])
print(my_list_copy)  # prints [[27], [13], [-11], [60], [39], [15], [17]]
print(my_list)  # prints [[27], [13], [-11], [60], [39], [15]]

However, if we decide to modify any of the nested lists, we’ll run into problems:

my_list_copy[0].append(12)
print(my_list_copy)  # prints [[27, 12], [13], [-11], [60], [39], [15], [17]]
print(my_list)  # prints [[27, 12], [13], [-11], [60], [39], [15]]

That’s because our copy operation only duplicated the outer list. In other words, we created two separate lists, but each list stores the same exact references. Modifying a reference in one list modifies it in the other list.

A deep copy method would make sure to copy both the outer list and the inner list. Keep that in mind as we move forward.

Solutions

If we want to clone a list, we have several options. Let’s take a look.

Clone a List by Brute Force

As always, Python offers several quick solutions to this problem. However, before we get to those, I want to actually examine cloning from a beginner’s perspective. In other words, let’s skip the API for now and try to implement our own cloning function:

def clone(my_list):
    my_list_clone = list()
    for item in my_list:
        my_list_clone.append(item)
    return my_list_clone

That seems simple enough. Basically, we just iterate over the list and copy each item into the new list. In fact, we can even make this solution more pythonic:

def clone(my_list):
    return [item for item in my_list]

How’s that for a one-liner? The problem is this method doesn’t perform a deep clone. Unfortunately, implementing deep clone by hand is a bit out of scope for this tutorial, but I challenge you to try it yourself. As a hint, you’ll basically want to build a recursive copy function.

Clone a List Using a Slice

If you thought the comprehension was slick, wait until you see this slice:

my_list = [27, 13, -11, 60, 39, 15]
my_duplicate_list = my_list[:]  # done

If you’re unfamiliar with slices, basically this takes a “subset” of the list from end-to-end. Normally, we would use slices like this:

my_list[:4]  # [27, 13, -11, 60]
my_list[3:]  # [60, 39, 15]

Without indices, the slice will duplicate the entire list. Again, however, this will not perform a deep copy.

Clone a List Using the List Constructor

In the world of software design patterns, there’s a creation pattern known as the copy constructor. Instead of taking a set of input parameters for construction, a copy constructor takes a reference to an initialized object and produces a copy of it. Luckily for us, Python provides a copy constructor for lists:

my_list = [27, 13, -11, 60, 39, 15]
my_duplicate_list = list(my_list)

Unfortunately, even this method does not provide a deep copy, but it is much more readable than the slice.

Clone a List Using the Copy Package

Python wouldn’t be Python without its endless package collection. As a result, you can probably imagine there’s some API we can leverage to perform the copying for us. After all, why should we be reinventing the wheel? Here’s how it works:

import copy
my_list = [27, 13, -11, 60, 39, 15]
my_duplicate_list = copy.copy(my_list)

Due to the generic nature of this method, we take a bit of a hit in performance. That said, it’s quite clear what we’re trying to accomplish here. Unfortunately, we still fail to produce a deep copy. Thankfully, the copy package has a solution for that:

my_list = [[27], [13], [-11], [60], [39], [15]]
my_duplicate_list = copy.deepcopy(my_list)

At long last, we have achieved a true deep copy of our nested lists. Of course, deep copy is entirely overkill if the list is only one layer deep.

Clone a List Using Multiplication

Honestly, I hesitated putting this one in here because it’s simply ridiculous, but it’s a fun abuse of the multiplication operator:

my_list = [27, 13, -11, 60, 39, 15]
my_list_copy = my_list * 1

Again, this does not perform a deep copy, but that’s hardly the point. We just used the multiplication operator to duplicate a list. Normally, we would use the multiplication operator to populate a list:

my_list = [0] * 100  # a list with 100 zeroes

Instead, we’ve decided to abuse it for the purposes of generating a list copy. If you think this is funny, take a look at this list of strange language features on Stack Overflow. After writing this section, I stumbled upon that article while trying to find other ways to abuse Python language features.

A Little Recap

With this installment of Python Bytes, we’re finally starting to get into some more interesting language features and topics. As a result, we’re finding a lot of ways to solve the same problem – some good, some bad. At any rate, here are all the ways we can clone a list in Python:

my_list = [27, 13, -11, 60, 39, 15]

# Clone a list by brute force
my_duplicate_list = [item for item in my_list]

# Clone a list with a slice
my_duplicate_list = my_list[:]

# Clone a list with the list constructor
my_duplicate_list = list(my_list)  # preferred method

# Clone a list with the copy package
import copy
my_duplicate_list = copy.copy(my_list)
my_deep_duplicate_list = copy.deepcopy(my_list)

# Clone a list with multiplication?
my_duplicate_list = my_list * 1  # do not do this

All of these methods will get the job done, but only one of these methods will actually perform a deep copy if needed. At any rate, we’re done here.

If you found this article helpful, consider sharing it on social media or leaving a comment below. Until next time!

Series Navigation← How to Check if a List is Empty in PythonHow to Get the Last Item of a List in Python →
Advertisements

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.