How to Copy a List in Python (5 Techniques w/ Examples)
Lists are commonly used data structures in Python. We will often encounter situations wherein we need to make a copy of a list, and you may ask yourself, "How can I copy a list in Python?" or "Which copy method suits my requirements best?"
This tutorial will teach you how to copy or clone a list using several different techniques:
- The assignment operator
- The slicing syntax
- The list.copy() method
- The copy.copy() function
- The copy.deepcopy() function
We will also discuss their usage and technical aspects in detail.
Copy a List Using the Assignment Operator
Suppose you use the assignment operator (=) to copy a list by assigning an existing list variable to a new list variable. In this case, you’re not actually creating a copy of the list; you’re just creating an alias that points to the exact same location in memory where the original list object exists. Let’s expand on the details and look closer.
Suppose we have the list variable, org_list , defined as follows:
Then, we assign it to a new variable, cpy_list , hoping to make a copy of it for future use:
However, you need to know the variable cpy_list isn’t a true copy of the original list. You may ask, "Why isn’t it a true copy of the original list?" This is a great question because, as you’ll see below, printing these two variables returns the exact the same values.
As expected, the lists contain the same values. But, let’s see what happens if we modify the original list.
Any modification to the original list will change the copied list, too.
The following illustration shows what’s happening once the source code is executed.
In fact, when you assign one variable to another, both variables are referencing the same object in memory, not two separate ones. This means both variables point to the same object via their references. When more than one variable references the same object, it’s called a shared reference or object.
Any modification to a shared mutable object via one of the variables that points to it affects the other variable that references the same object.
So, using the assignment operator doesn’t make a true copy of a list; it just creates an alias for the same object in memory.
But what if we want to make an independent copy of a list? In the following section, we’ll learn how to make shallow copies of a list.
The Shallow Copy Techniques
We just learned that assignments always store references to objects and don’t make an actual copy of those objects. However, it’s essential to know that changing a mutable object affects other objects that use the same reference in our code. So, we need to let Python know explicitly to copy an object if we want more than just a copy of the reference to that object. Generally speaking, there are two ways of making an independent copy of a list: a shallow copy and a deep copy. This section will discuss shallow copy and the different ways of implementing it.
Simply put, making a shallow copy of a compound list creates a new compound list and uses the references to the objects that the original list used.
NOTE: A compound object is an object that contains other objects, e.g., lists or dictionaries.
Shallow Copy Using List Slicing
To understand the shallow copy concept, let’s begin with an example. Assume we have a compound list as follows:
Then, we can create a shallow copy of it using list slicing syntax:
If we run the following print statements, we can see, both return exactly the same values.
Now, let’s append a new item to the original list and run the print statements again:
The modification doesn’t affect the copied list. But, this isn’t the whole story. Let’s try another scenario and change one of the items in the nested list to see what happens:
Although making a shallow copy of a list produces a true copy of the original list, any modifications to the nested elements within it will be reflected in both lists. The reason is that the nested list in the copied list uses the same shared reference as the one in the original list. In other words, the nested lists in the copied list are tied to the nested lists in the original list. This is why we call it a shallow copy — because only a new top-level object is created while anything deeper uses a shared reference with the original list.
Now, let’s look at some other ways of making shallow copies of a list.
The Python list.copy() Method
Earlier, we discussed creating a shallow copy via the slicing syntax. In this section, we’ll learn about a built-in method that Python programmers commonly use for copying lists. The Python copy() method returns a shallow copy of the list without taking any parameters. Let’s try it out:
Although org_list and cpy_list have the same values, as the output from the id() function shows, they end up in different locations in memory. However, exposing the memory addresses of the inner lists in both the original and copied lists reveals that they refer to the same location in memory, meaning we made a shallow copy of the original list.
The Python copy.copy() Function
The other useful way of making a shallow copy of a list is the copy.copy() function. To use it, we import the copy module and then pass the list we want to copy to the copy.copy() function. Let’s try it out:
Now, let’s append a new item to the original list, print both lists again, and check the output; nevertheless, we can predict the output before running the code below.
The copy.copy() method has made a true copy of the original list. However, it’s still a shallow copy, and the nested lists refer to exactly the same memory location. In other words, the copy.copy() function only makes top-level copies and doesn’t copy nested objects. So, any modifications in either the original or copied list’s nested objects reflects in the other list’s nested objects.
What if we want a fully independent copy of a deeply nested list? In the next section, we’ll discuss how to perform a deep copy in Python.
The Python copy.deepcopy() Function
The copy.deepcopy() function recursively traverses a list to make copies of each of its nested objects. In other words, it makes a top-level copy of a list and then recursively adds copies of the nested objects from the original list into the new copy. This produces a fully independent copy from the original list, and any changes made to the nested objects of either will not be reflected in the other.
Like the copy.copy() function, the copy.deepcopy() function belongs to the copy module. Let’s try it out:
The output of the code above clearly shows that copy.deepcopy() has made a true copy of the original list, and even if we modify the inner list of the original list, it won’t be reflected in the deep copied list.
The code above shows that when we create a deep copy of a list, it also makes true copies of the nested objects. As mentioned earlier, the recursive deep copy produces a truly independent copy of the original list, which is why the inner lists in the original and copied lists point to two different memory locations. Obviously, any changes made to the inner list of one won’t be reflected in the other.
Conclusion
This tutorial discussed several different ways for copying a list in Python, such as the assignment operator, list slicing syntax, list.copy() , copy.copy() , and copy.deepcopy functions. Also, we discussed shallow and deep copies. I hope this tutorial helps you to better understand the different ways of copying lists in Python because they are critical to becoming a Pythonista.
About the author
Mehdi Lotfinejad
Mehdi is a Senior Data Engineer and Team Lead at ADA. He is a professional trainer who loves writing data analytics tutorials.
copy — Shallow and deep copy operations¶
Assignment statements in Python do not copy objects, they create bindings between a target and an object. For collections that are mutable or contain mutable items, a copy is sometimes needed so one can change one copy without changing the other. This module provides generic shallow and deep copy operations (explained below).
Return a shallow copy of x.
Return a deep copy of x.
exception copy. Error ¶
Raised for module specific errors.
The difference between shallow and deep copying is only relevant for compound objects (objects that contain other objects, like lists or class instances):
A shallow copy constructs a new compound object and then (to the extent possible) inserts references into it to the objects found in the original.
A deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found in the original.
Two problems often exist with deep copy operations that don’t exist with shallow copy operations:
Recursive objects (compound objects that, directly or indirectly, contain a reference to themselves) may cause a recursive loop.
Because deep copy copies everything it may copy too much, such as data which is intended to be shared between copies.
The deepcopy() function avoids these problems by:
keeping a memo dictionary of objects already copied during the current copying pass; and
letting user-defined classes override the copying operation or the set of components copied.
This module does not copy types like module, method, stack trace, stack frame, file, socket, window, or any similar types. It does “copy” functions and classes (shallow and deeply), by returning the original object unchanged; this is compatible with the way these are treated by the pickle module.
Shallow copies of dictionaries can be made using dict.copy() , and of lists by assigning a slice of the entire list, for example, copied_list = original_list[:] .
Classes can use the same interfaces to control copying that they use to control pickling. See the description of module pickle for information on these methods. In fact, the copy module uses the registered pickle functions from the copyreg module.
In order for a class to define its own copy implementation, it can define special methods __copy__() and __deepcopy__() . The former is called to implement the shallow copy operation; no additional arguments are passed. The latter is called to implement the deep copy operation; it is passed one argument, the memo dictionary. If the __deepcopy__() implementation needs to make a deep copy of a component, it should call the deepcopy() function with the component as first argument and the memo dictionary as second argument. The memo dictionary should be treated as an opaque object.
Discussion of the special methods used to support object state retrieval and restoration.
Копирование массива в Python. Метод copy для копирования массива. Представление массива
В этой статье мы поговорим, как скопировать массив в Python и чем копирование массива отличается от представления массива. Также рассмотрим поверхностное и глубокое копирование объектов в Python.
Работая с массивами в Python, вы наверняка сталкивались с ситуацией, когда при использовании некоторых функций, возвращающих какой-нибудь результат, с исходным массивом не происходит ничего. Как в примере ниже:
Всё дело в том, что в библиотеке NumPy есть два понятия касаемо массива: копия и представление. И это разные вещи. Посмотрите на код ниже:
На самом деле, всё просто. Когда мы выполняем присваивание b = a, никакого копирования данных на деле не происходит. В памяти компьютера всё так же один массив, а переменные a и b — это даже не переменные, а указатели, указывающие на одни и те же данные. Таким образом, мы обращаемся по разным указателям к одним и тем же данным в памяти и видим в результате одно и то же.
Хорошо, a и b являются указателями, но что тогда с переменной с? На деле, это тоже указатель, ссылающийся на ту же область памяти с данными, правда, представлены эти данные в иной форме. Вот мы и подошли к понятию представления массива, которое существует в NumPy. Действительно, те же данные можно представить в разной форме:
На деле, можно говорить о копировании, если данные в памяти компьютера физически копируются в другое место. Но, как мы уже убедились, операция присваивания копирование не выполняет. Также мы поняли, что те же самые данные могут иметь различные представления.
Присваивание не копирует массивы в Python
Итак, простое присваивание никаких копий массива не выполняет, и это первое, что стоит уяснить. Может показаться, что это всего лишь прихоть создателей Python и NumPy, но всё не так просто. Если бы ситуация обстояла иначе, мы бы работали с памятью напрямую, а отсутствие автоматического копирования во время присваивания — совсем небольшая плата за лёгкость и простоту языка программирования Python.
Давайте приведём ещё парочку примеров на эту тему:
Обратите внимание, что массивы a и b в действительности являются одним и тем же массивом с такими же данными и типом данных.
Таким образом, у нас есть массив b и массив a, но нельзя забывать о том, что это, по сути, один и тот же массив.
Так как же копировать массивы в Python?
Чтобы сделать полную копию массива в Python, используют метод copy. Он выполняет копирование не только данных массива, но и всех его свойств.
После применения метода copy мы можем говорить о массивах a и b, как о разных массивах и копиях друг друга. Да, эти массивы имеют одинаковые данные, но эти данные не являются одними и теми же. Теперь действительно массив b является копией массива a, и именно это называется копированием массива в терминологии NumPy.
Представление массива
Итак, теперь мы знаем, как сделать копию массива посредством метода copy. Но бывают ситуации, когда нам не нужна копия массива, а нужен тот же массив но с иными размерами. Речь идёт, как вы уже догадались, о другом представлении исходного массива.
Для этих целей в NumPy есть метод ndarray.view() . Он создаёт новый объект массива, просматривающий данные исходного, однако изменение размеров одного массива не приводит к изменению размеров другого.
Обычно, функции которые меняют форму и порядок элементов в Пайтон-массивах возвращают не копию массива, а именно его представление:
Также представлениями массивов в Python являются срезы массивов:
Обратите внимание, что когда мы говорим о том, что массив b является представлением массива a, мы подразумеваем, что вне зависимости от вида и формы массива b он включает в себя те же данные в памяти, что и наш массив a. Таким образом, изменение элементов в одном из массивов приведёт, соответственно, к изменениям в другом.
Глубокое и поверхностное копирование объектов с помощью copy
Как мы уже хорошо уяснили, операция присваивания не приводит к копированию объекта, а лишь создаёт ссылку на этот объект. Но если мы работаем с изменяемыми коллекциями или коллекциями, которые содержат изменяемые элементы, нам может понадобиться такая копия, которую мы сможем изменить, не меняя оригинал. Здесь нам тоже поможет copy, выполняющий как поверхностное, так и глубокое копирование: • copy.copy(a) — возвращает поверхностную копию a; • copy.deepcopy(a) — возвращает полную копию a.
Если же объект скопировать невозможно, возникает исключение copy.error. В принципе, разница между глубоким и поверхностным копированием существенна лишь для составных объектов, которые содержат изменяемые объекты (допустим, список списков). При этом: 1) поверхностная копия позволяет создать новый составной объект, а потом (если это возможно) вставляет в него ссылки на объекты, которые находятся в оригинале; 2) глубокая копия позволяет создать новый составной объект, а потом рекурсивно вставляет в него копии объектов, которые находятся в оригинале.
При выполнении глубокого копирования возможны проблемы (их нет у поверхностного копирования): — рекурсивные объекты могут привести к рекурсивному циклу; — т. к. глубокая копия копирует всё, она способна скопировать слишком много, к примеру, административные структуры данных.
Однако в случае возникновения проблем нам поможет функция deepcopy, которая устраняет эти сложности: — посредством хранения «memo» словаря объектов; — позволяя классам, которые определяет пользователь, переопределять операцию копирования либо набор копируемых компонентов.
В результате, не копируются типы вроде классов, функций, модулей, методов, стековых кадров, окон, сокетов и т. п.
Что же, теперь, надеемся, вы получили представление о копировании массивов и объектов в Python. Если хотите знать больше, к вашим услугам специализированный курс для продвинутых разработчиков:
How to Copy a List in Python: Slice, Copy, and More
Hello again! Welcome to the sixth installment of the How to Python series. Today, we’re going to learn how to clone or copy a list in Python. Unlike most articles in this series, there are actually quite a few options—some better than others.
In short, there are so many different ways to copy a list. In this article alone, we share eight solutions. If you’re looking for something safe, use the copy method (i.e. my_list.copy() ). Otherwise, feel free to try slicing (i.e. my_list[:] ) or the list constructor (i.e. list(my_list) ).
Table of Contents
Video Summary
If you’re not interested in digging through this article, I’ve shared all the material in a YouTube video. In addition to live coding most of the solutions to the list copying problem, I’ve also share some performance metrics as well as my solution to the challenge below. If for nothing else, I’d love it if you ran over to YouTube and boosted my metrics a little bit (like, comment, subscribe, etc.).
Problem Introduction
Imagine that we have a list:
And, we want to create a duplicate of this list, so we can modify their contents independently:
How would we go about doing that? Well, 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:
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:
So, clearly that’s not what we want. Instead of duplicating our list, we’ve 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:
If we decide to perform a simple copy on this list, we’ll end up with some strange behavior:
Alright, so no problems yet. In fact, we can even append information to the new list without any problems:
However, if we decide to modify any of the nested lists, we’ll run into problems:
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.
Copy 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:
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:
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.
Copy a List Using a Slice
If you thought the comprehension was slick, wait until you see this slice:
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:
Without indices, the slice will duplicate the entire list. Again, however, this will not perform a deep copy.
Copy 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:
Unfortunately, even this method does not provide a deep copy, but it is much more readable than the slice.
Copy a List Using a Starred Expression
Recently, dev.to user, Leah Einhorn, tipped me off on yet another way to copy a list in Python:
For a lack of a better term, I’ll go ahead and call this solution a “starred expression” because that’s the syntax error I got when I messed it up:
That said, I think the technical term for this solution would be iterable unpacking which I’ve talked about in the following articles:
At any rate, this works by expanding the list into comma separated arguments. In other words, instead of storing the list inside another list, we actually unpack the list into individual items and load them directly into a new list.
As is the case with most solutions in this list, iterable unpacking also falls prey to the same shallow copy issues. As a result, you wouldn’t be able to use this solution to copy a list of lists (see the challenge below).
Copy a List Using the Copy Function
As it turns out, Python 3.3+ includes a copy function for lists directly. To use it, call copy() on the list and store the results:
In my opinion, this is the most readable option, and I believe it’s also the most efficient version. In fact, the performance should be similar to the slice. However, Python changes a lot, so the other methods mentioned here may be just as performant—check out my benchmarks below for more info.
Copy 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:
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:
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. See the challenge below if you’re interested in other ways of accomplishing a deep copy.
Copy 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:
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:
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.
Performance
If you haven’t watched the video summary yet, now would be a great time to check out the performance section. After all, I’m snagging all the metrics from there.
At any rate, to check performance, I like to use the timeit library which allows us to check the speed of a code snippet. And if we run all our code snippets, we’ll get a nice relative comparison. To start, we have to build up our set of strings:
With these strings in place, it’s just a matter of running them using the timeit library:
And, there we have it! All eight solutions fully tested. Naturally, I was intrigued by the fastest solutions, so I decided to see how they scaled. Here’s the updated setup string which generates a list of 1000 items:
Here are the updated test results with the top 4 solutions:
Overall, it looks like all four solutions scale at about the same rate. In other words, there’s not much of a difference beyond initial overhead. Perhaps they get much worse with more items, but I don’t really have the patience to test any further. Perhaps someone can take a look for us!
At any rate, if you’re interested in fast solutions, check out the slice, starred expression, copying, and multiplication solutions. Of course, I’d say the built-in copy function is the way to go—regardless of speed.
Challenge
Now that we’ve covered several copying mechanisms in Python, I thought would be fun to propose a bit of a challenge. In particular, write some code to duplicate a nested list. For example:
Then, you should be able to confirm you copy worked by testing the sublists for identity:
When you’re ready, share your solution in the comments. It should work for any type of nested list, but we’ll assume a depth of 1 (i.e. a 2-dimensional list). In addition, we’ll assume items in the deepest list are primitives or at least immutable (i.e. numbers, strings, etc.). Finally, you cannot use the deepcopy function. Instead, you should implement your own.
Here’s my solution!
Copying a list in Python is tough, but copying a nested list is a slightly more nasty #RenegadePython
challenge. Here’s one way to do it. pic.twitter.com/Do8KkLi2Ff
— Jeremy Grifski (@RenegadeCoder94) April 19, 2020
If you’d like to try your hand at this challenge, share your solution on Twitter with the hashtag #RenegadePython. If I see it, I’ll give it a share!
A Little Recap
With this installment of How to Python, 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:
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!
How to Python (42 Articles)—Series Navigation
The How to Python tutorial series strays from the usual in-depth coding articles by exploring byte-sized problems in Python. In this series, students will dive into unique topics such as How to Invert a Dictionary, How to Sum Elements of Two Lists, and How to Check if a File Exists.
Each problem is explored from the naive approach to the ideal solution. Occasionally, there’ll be some just-for-fun solutions too. At the end of every article, you’ll find a recap full of code snippets for your own use. Don’t be afraid to take what you need!
If you’re not sure where to start, I recommend checking out our list of Python Code Snippets for Everyday Problems. In addition, you can find some of the snippets in a Jupyter notebook format on GitHub,
If you have a problem of your own, feel free to ask. Someone else probably has the same problem. Enjoy How to Python!
Jeremy grew up in a small town where he enjoyed playing soccer and video games, practicing taekwondo, and trading Pokémon cards. Once out of the nest, he pursued a Bachelors in Computer Engineering with a minor in Game Design. After college, he spent about two years writing software for a major engineering company. Then, he earned a master’s in Computer Science and Engineering. Today, he pursues a PhD in Engineering Education in order to ultimately land a teaching gig. In his spare time, Jeremy enjoys spending time with his wife, playing Overwatch and Phantasy Star Online 2, practicing trombone, watching Penguins hockey, and traveling the world.
Recent Posts
Life is a gift worth celebrating even after that life is gone. Let me share a little bit of my experience with one of these celebrations.
Part of the PhD journey is conducting a study which you can write about for a dissertation. Right now, I’m in the middle of data collection, and I wanted to share how it was going.
About Me
Welcome to The Renegade Coder, a coding curriculum website run by myself, Jeremy Grifski. If you like what you see, consider subscribing to my newsletter. Right now, new subscribers will receive a copy of my Python 3 Beginner Cheat Sheet. If newsletters aren’t your thing, there are at least 4 other ways you can help grow The Renegade Coder. I appreciate the support!