如何克隆列表,使其在分配后不会意外更改?
使用 new_list = my_list
时,对 new_list
的任何修改每次都会更改 my_list
。这是为什么?如何克隆或复制列表来防止这种情况发生?例如:
>>> my_list = [1, 2, 3]
>>> new_list = my_list
>>> new_list.append(4)
>>> my_list
[1, 2, 3, 4]
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(24)
new_list = my_list
实际上并没有创建第二个列表。赋值只是复制对列表的引用,而不是实际的列表,因此new_list
和my_list
在赋值后都引用同一个列表。要实际复制列表,您有多种选择:
您可以使用内置的
list.copy()
方法(自 Python 3.3 起可用):您可以对其进行切片:
Alex Martelli 的意见(至少早在 2007 年)关于这一点,这是一种奇怪的语法,使用它没有任何意义。 ;)(他认为下一篇更具可读性)。
您可以使用内置的
list()
< /a> 构造函数:您可以使用通用
copy.copy()
:
这比
list()
慢一点,因为它必须首先找出old_list
的数据类型。如果您还需要复制列表的元素,请使用通用 <代码>copy.deepcopy():
显然这是最慢且最需要内存的方法,但有时是不可避免的。这是递归操作的;它将处理任意数量的嵌套列表(或其他容器)的级别。
示例:
结果:
new_list = my_list
doesn't actually create a second list. The assignment just copies the reference to the list, not the actual list, so bothnew_list
andmy_list
refer to the same list after the assignment.To actually copy the list, you have several options:
You can use the built-in
list.copy()
method (available since Python 3.3):You can slice it:
Alex Martelli's opinion (at least back in 2007) about this is, that it is a weird syntax and it does not make sense to use it ever. ;) (In his opinion, the next one is more readable).
You can use the built-in
list()
constructor:You can use generic
copy.copy()
:This is a little slower than
list()
because it has to find out the datatype ofold_list
first.If you need to copy the elements of the list as well, use generic
copy.deepcopy()
:Obviously the slowest and most memory-needing method, but sometimes unavoidable. This operates recursively; it will handle any number of levels of nested lists (or other containers).
Example:
Result:
Felix 已经提供了一个很好的答案,但我想我应该对各种方法进行速度比较:
copy.deepcopy(old_list)
Copy()
使用 deepcopy 复制类的方法Copy()
方法不复制类(仅字典/列表/元组)对于 old_list 中的项目:new_list.append(item)
[i for i in old_list]
(a href="http://docs .python.org/tutorial/datastructs.html#list-compressiveons" rel="noreferrer">列表理解)copy.copy(old_list)
list(old_list) )
new_list = []; new_list.extend(old_list)
old_list[:]
(列表切片)所以最快的是列表切片。但请注意,
copy.copy()
、list[:]
和list(list)
与copy.deepcopy() 不同
和 python 版本不会复制列表中的任何列表、字典和类实例,因此如果原始版本发生更改,它们也会在复制的列表中发生更改,反之亦然。(如果有人感兴趣或想提出任何问题,这是脚本:)
Felix already provided an excellent answer, but I thought I'd do a speed comparison of the various methods:
copy.deepcopy(old_list)
Copy()
method copying classes with deepcopyCopy()
method not copying classes (only dicts/lists/tuples)for item in old_list: new_list.append(item)
[i for i in old_list]
(a list comprehension)copy.copy(old_list)
list(old_list)
new_list = []; new_list.extend(old_list)
old_list[:]
(list slicing)So the fastest is list slicing. But be aware that
copy.copy()
,list[:]
andlist(list)
, unlikecopy.deepcopy()
and the python version don't copy any lists, dictionaries and class instances in the list, so if the originals change, they will change in the copied list too and vice versa.(Here's the script if anyone's interested or wants to raise any issues:)
我被告知 Python 3.3+ 添加
list.copy()
方法,该方法应该与切片一样快:I've been told that Python 3.3+ adds the
list.copy()
method, which should be as fast as slicing:在 Python 3 中,可以使用以下方式进行浅复制:
在 Python 2 和 3 中,您可以获得原始片段的完整切片的浅复制:
说明
复制列表有两种语义方法。浅复制创建相同对象的新列表,深复制创建包含新的等效对象的新列表。
浅表复制
浅表复制仅复制列表本身,列表本身是对列表中对象的引用的容器。如果自身包含的对象是可变的并且其中一个对象发生了更改,则该更改将反映在两个列表中。
在 Python 2 和 3 中可以采用不同的方法来执行此操作。Python 2 的方法也适用于 Python 3。 Python
2
在 Python 2 中,制作列表浅表副本的惯用方法是使用原始列表的完整切片:
您还可以通过列表构造函数传递列表来完成同样的事情,
但使用构造函数的效率较低:
3
在 Python 3 中,列表获取
list.copy
方法:在 Python 3.5 中:
Python 另一个指针不进行复制
my_list
只是一个指向内存中实际列表的名称。当您说new_list = my_list
时,您并不是在制作副本,您只是添加了另一个指向内存中原始列表的名称。当我们复制列表时,我们可能会遇到类似的问题。该列表只是指向内容的指针数组,因此浅拷贝仅复制指针,因此您有两个不同的列表,但它们具有相同的内容。要复制内容,您需要深层复制。
深层复制
要创建列表的深层副本,在 Python 2 或 3 中,请使用
deepcopy<
copy
模块中的 /code>:演示这如何允许我们创建新的子列表:
因此我们看到深度复制的列表是与原始列表完全不同的列表。您可以推出自己的函数 - 但不要这样做。您可能会产生使用标准库的深度复制函数不会出现的错误。
不要使用
eval
您可能会看到这被用作深度复制的一种方式,但不要这样做:
在 64 位 Python 2.7 中:
在 64 位 Python 3.5 中:
In Python 3, a shallow copy can be made with:
In Python 2 and 3, you can get a shallow copy with a full slice of the original:
Explanation
There are two semantic ways to copy a list. A shallow copy creates a new list of the same objects, a deep copy creates a new list containing new equivalent objects.
Shallow list copy
A shallow copy only copies the list itself, which is a container of references to the objects in the list. If the objects contained themselves are mutable and one is changed, the change will be reflected in both lists.
There are different ways to do this in Python 2 and 3. The Python 2 ways will also work in Python 3.
Python 2
In Python 2, the idiomatic way of making a shallow copy of a list is with a complete slice of the original:
You can also accomplish the same thing by passing the list through the list constructor,
but using the constructor is less efficient:
Python 3
In Python 3, lists get the
list.copy
method:In Python 3.5:
Making another pointer does not make a copy
my_list
is just a name that points to the actual list in memory. When you saynew_list = my_list
you're not making a copy, you're just adding another name that points at that original list in memory. We can have similar issues when we make copies of lists.The list is just an array of pointers to the contents, so a shallow copy just copies the pointers, and so you have two different lists, but they have the same contents. To make copies of the contents, you need a deep copy.
Deep copies
To make a deep copy of a list, in Python 2 or 3, use
deepcopy
in thecopy
module:To demonstrate how this allows us to make new sub-lists:
And so we see that the deep copied list is an entirely different list from the original. You could roll your own function - but don't. You're likely to create bugs you otherwise wouldn't have by using the standard library's deepcopy function.
Don't use
eval
You may see this used as a way to deepcopy, but don't do it:
In 64 bit Python 2.7:
on 64 bit Python 3.5:
让我们从头开始探讨这个问题。
因此,假设您有两个列表:
我们必须复制这两个列表,现在从第一个列表开始:
所以首先让我们尝试将变量
copy
设置为我们的原始列表list_1:
现在,如果您认为复制了 list_1,那么您就错了。 id 函数可以告诉我们两个变量是否可以指向同一个对象。让我们试试这个:
输出是:
两个变量是完全相同的参数。你很惊讶吗?
正如我们所知,Python 不会在变量中存储任何内容,变量只是引用对象,而对象存储值。这里的对象是一个列表,但我们通过两个不同的变量名创建了对同一对象的两个引用。这意味着两个变量都指向同一个对象,只是名称不同。
当您执行
copy = list_1
时,它实际上正在执行:图像中的 list_1 和 copy 是两个变量名称,但两个变量的对象是相同的,即
list
。因此,如果您尝试修改复制的列表,那么它也会修改原始列表,因为该列表只有一个,无论您从复制的列表还是原始列表中执行操作,您都会修改该列表:
输出:
所以它修改了原始列表list:
现在让我们转向复制列表的 Pythonic 方法。
这个方法解决了我们遇到的第一个问题:
所以我们可以看到两个列表都有不同的 id,这意味着两个变量都指向不同的对象。所以这里实际发生的是:
现在让我们尝试修改列表,看看是否仍然面临之前的问题:
输出为:
如您所见,它只修改了复制的列表。这意味着它起作用了。
你认为我们已经完成了吗?不,让我们尝试复制我们的嵌套列表。
list_2
应引用另一个对象,该对象是list_2
的副本。让我们检查一下:我们得到了输出:
现在我们可以假设两个列表都指向不同的对象,所以现在让我们尝试修改它,看看它给出了我们想要的结果:
这给了我们输出:
这可能看起来有点令人困惑,因为我们之前使用的方法同样有效。让我们试着理解这一点。
当您这样做时:
您仅复制外部列表,而不复制内部列表。我们可以再次使用 id 函数来检查这一点。
输出是:
当我们执行
copy_2 = list_2[:]
时,会发生这种情况:< img src="https://i.sstatic.net/3hPti.jpg" alt="在此处输入图像描述">
它创建列表的副本,但仅创建外部列表副本,而不创建嵌套列表副本。两个变量的嵌套列表是相同的,因此如果您尝试修改嵌套列表,那么它也会修改原始列表,因为两个列表的嵌套列表对象是相同的。
解决办法是什么?解决方案是“deepcopy”函数。
让我们检查一下:
两个外部列表都有不同的 ID。让我们在内部嵌套列表上尝试一下。
输出是:
如您所见,两个 ID 不同,这意味着我们可以假设两个嵌套列表现在都指向不同的对象。
这意味着当您执行
deep = deepcopy(list_2)
时实际会发生什么:两个嵌套列表都指向不同的对象,并且它们现在有单独的嵌套列表副本。
现在让我们尝试修改嵌套列表,看看它是否解决了前面的问题:
它输出:
正如你所看到的,它没有修改原始的嵌套列表,它只修改了复制的列表。
Let's start from the beginning and explore this question.
So let's suppose you have two lists:
And we have to copy both lists, now starting from the first list:
So first let's try by setting the variable
copy
to our original list,list_1
:Now if you are thinking copy copied the list_1, then you are wrong. The
id
function can show us if two variables can point to the same object. Let's try this:The output is:
Both variables are the exact same argument. Are you surprised?
So as we know, Python doesn't store anything in a variable, Variables are just referencing to the object and object store the value. Here object is a
list
but we created two references to that same object by two different variable names. This means that both variables are pointing to the same object, just with different names.When you do
copy = list_1
, it is actually doing:Here in the image list_1 and copy are two variable names, but the object is same for both variable which is
list
.So if you try to modify copied list then it will modify the original list too because the list is only one there, you will modify that list no matter you do from the copied list or from the original list:
Output:
So it modified the original list:
Now let's move onto a Pythonic method for copying lists.
This method fixes the first issue we had:
So as we can see our both list having different id and it means that both variables are pointing to different objects. So what actually going on here is:
Now let's try to modify the list and let's see if we still face the previous problem:
The output is:
As you can see, it only modified the copied list. That means it worked.
Do you think we're done? No. Let's try to copy our nested list.
list_2
should reference to another object which is copy oflist_2
. Let's check:We get the output:
Now we can assume both lists are pointing different object, so now let's try to modify it and let's see it is giving what we want:
This gives us the output:
This may seem a little bit confusing, because the same method we previously used worked. Let's try to understand this.
When you do:
You're only copying the outer list, not the inside list. We can use the
id
function once again to check this.The output is:
When we do
copy_2 = list_2[:]
, this happens:It creates the copy of list, but only outer list copy, not the nested list copy. The nested list is same for both variable, so if you try to modify the nested list then it will modify the original list too as the nested list object is same for both lists.
What is the solution? The solution is the
deepcopy
function.Let's check this:
Both outer lists have different IDs. Let's try this on the inner nested lists.
The output is:
As you can see both IDs are different, meaning we can assume that both nested lists are pointing different object now.
This means when you do
deep = deepcopy(list_2)
what actually happens:Both nested lists are pointing different object and they have separate copy of nested list now.
Now let's try to modify the nested list and see if it solved the previous issue or not:
It outputs:
As you can see, it didn't modify the original nested list, it only modified the copied list.
已经有很多答案告诉您如何制作正确的副本,但没有一个答案说明为什么您的原始“副本”失败。
Python 不将值存储在变量中;它将名称绑定到对象。您最初的分配获取了
my_list
引用的对象,并将其绑定到new_list
。无论您使用哪个名称,仍然只有一个列表,因此将其引用为my_list
时所做的更改将在将其引用为new_list
时保留。这个问题的其他每个答案都为您提供了创建新对象以绑定到 new_list 的不同方法。列表中的每个元素的作用就像一个名称,因为每个元素非独占地绑定到一个对象。浅拷贝创建一个新列表,其元素绑定到与之前相同的对象。
要使列表复制更进一步,请复制列表引用的每个对象,并将这些元素副本绑定到新列表。
这还不是深层复制,因为列表的每个元素都可能引用其他对象,就像列表绑定到其元素一样。要递归复制列表中的每个元素,然后复制每个元素引用的每个其他对象,依此类推:执行深度复制。
有关复制中极端情况的更多信息,请参阅文档。
There are many answers already that tell you how to make a proper copy, but none of them say why your original 'copy' failed.
Python doesn't store values in variables; it binds names to objects. Your original assignment took the object referred to by
my_list
and bound it tonew_list
as well. No matter which name you use there is still only one list, so changes made when referring to it asmy_list
will persist when referring to it asnew_list
. Each of the other answers to this question give you different ways of creating a new object to bind tonew_list
.Each element of a list acts like a name, in that each element binds non-exclusively to an object. A shallow copy creates a new list whose elements bind to the same objects as before.
To take your list copy one step further, copy each object that your list refers to, and bind those element copies to a new list.
This is not yet a deep copy, because each element of a list may refer to other objects, just like the list is bound to its elements. To recursively copy every element in the list, and then each other object referred to by each element, and so on: perform a deep copy.
See the documentation for more information about corner cases in copying.
使用
事物[:]
Use
thing[:]
Python 3.6 计时
以下是使用 Python 3.6.8 的计时结果。请记住,这些时间是相对的,而不是绝对的。
我坚持只做浅拷贝,并且还添加了一些 Python 2 中不可能的新方法,例如
list.copy()
(Python 3 切片等效)和两种形式的 列表解包(*new_list, = list
和new_list = [*list]
):我们可以可以看出,Python 2 的获胜者仍然表现出色,但并没有比 Python 3
list.copy()
优势太多,特别是考虑到后者具有出色的可读性。黑马是解包和重新打包方法 (
b = [*a]
),它比原始切片快约 25%,是其他解包方法 (*b, = a
)。b = a * 1
的表现也出奇的好。请注意,这些方法不会为列表以外的任何输入输出等效结果。它们都适用于可切片对象,少数适用于任何可迭代对象,但仅限于复制.copy() 适用于更通用的 Python 对象。
这是感兴趣的各方的测试代码(来自此处的模板 ):
Python 3.6 Timings
Here are the timing results using Python 3.6.8. Keep in mind these times are relative to one another, not absolute.
I stuck to only doing shallow copies, and also added some new methods that weren't possible in Python 2, such as
list.copy()
(the Python 3 slice equivalent) and two forms of list unpacking (*new_list, = list
andnew_list = [*list]
):We can see the Python 2 winner still does well, but doesn't edge out Python 3
list.copy()
by much, especially considering the superior readability of the latter.The dark horse is the unpacking and repacking method (
b = [*a]
), which is ~25% faster than raw slicing, and more than twice as fast as the other unpacking method (*b, = a
).b = a * 1
also does surprisingly well.Note that these methods do not output equivalent results for any input other than lists. They all work for sliceable objects, a few work for any iterable, but only
copy.copy()
works for more general Python objects.Here is the testing code for interested parties (Template from here):
Python 执行此操作的习惯用法是
newList = oldList[:]
Python's idiom for doing this is
newList = oldList[:]
所有其他贡献者都给出了很棒答案,当您有一个单一维度(分级)列表时,这些答案有效,但是到目前为止提到的方法中,只有
copy.deepcopy()
当您使用多维嵌套列表(列表的列表)时,可以克隆/复制列表,而不是让它指向嵌套的list
对象。虽然 Felix Kling 在他的回答中提到了这一点,但这个问题还有更多内容,并且可能有一个解决方法内置函数可能被证明是deepcopy
的更快替代方案。虽然
new_list = old_list[:]
、copy.copy(old_list)'
和 Py3kold_list.copy()
适用于单级列表,它们恢复为指向嵌套在old_list
和new_list
中的list
对象,并更改为list
之一code> 对象在另一个中永久存在。编辑:新信息曝光
正如其他人所说,使用
copy
模块和copy.deepcopy
存在重大性能问题<对于多维列表。All of the other contributors gave great answers, which work when you have a single dimension (leveled) list, however of the methods mentioned so far, only
copy.deepcopy()
works to clone/copy a list and not have it point to the nestedlist
objects when you are working with multidimensional, nested lists (list of lists). While Felix Kling refers to it in his answer, there is a little bit more to the issue and possibly a workaround using built-ins that might prove a faster alternative todeepcopy
.While
new_list = old_list[:]
,copy.copy(old_list)'
and for Py3kold_list.copy()
work for single-leveled lists, they revert to pointing at thelist
objects nested within theold_list
and thenew_list
, and changes to one of thelist
objects are perpetuated in the other.Edit: New information brought to light
As others have stated, there are significant performance issues using the
copy
module andcopy.deepcopy
for multidimensional lists.令我惊讶的是,这一点还没有被提及,所以为了完整起见......
您可以使用“splat 运算符”执行列表解包:
*
,这也将复制列表的元素。此方法的明显缺点是它仅在 Python 3.5+ 中可用。
不过,从时间角度来看,这似乎比其他常见方法表现更好。
It surprises me that this hasn't been mentioned yet, so for the sake of completeness...
You can perform list unpacking with the "splat operator":
*
, which will also copy elements of your list.The obvious downside to this method is that it is only available in Python 3.5+.
Timing wise though, this appears to perform better than other common methods.
new_list = my_list
尝试理解这一点。假设 my_list 位于堆内存中的 X 位置,即 my_list 指向 X。现在通过分配
new_list = my_list
你让new_list指向X。这称为浅拷贝。现在,如果您分配
new_list = my_list[:]
,您只需将 my_list 的每个对象复制到 new_list 即可。这称为深层复制。您可以执行此操作的其他方法是:
new_list = my_list
Try to understand this. Let's say that my_list is in the heap memory at location X, i.e., my_list is pointing to the X. Now by assigning
new_list = my_list
you're letting new_list point to the X. This is known as a shallow copy.Now if you assign
new_list = my_list[:]
, you're simply copying each object of my_list to new_list. This is known as a deep copy.The other ways you can do this are:
已经给出的答案中缺少一种独立于 python 版本的非常简单的方法,您可以在大多数时间使用该答案(至少我这样做):
但是,if my_list 包含其他容器(例如,嵌套列表),您必须按照复制库中上述答案中其他人的建议使用deepcopy。例如:
.奖励:如果您不想复制元素,请使用(又名浅复制):
让我们了解解决方案 #1 和解决方案 #2 之间的区别
正如您所看到的,解决方案 #1 工作得很好当我们不使用嵌套列表时。让我们检查一下当我们将解决方案 #1 应用于嵌套列表时会发生什么。
A very simple approach independent of python version was missing in already-given answers which you can use most of the time (at least I do):
However, if my_list contains other containers (for example, nested lists) you must use deepcopy as others suggested in the answers above from the copy library. For example:
.Bonus: If you don't want to copy elements use (AKA shallow copy):
Let's understand difference between solution #1 and solution #2
As you can see, solution #1 worked perfectly when we were not using the nested lists. Let's check what will happen when we apply solution #1 to nested lists.
我想发布一些与其他答案略有不同的内容。尽管这很可能不是最容易理解或最快的选项,但它提供了深度复制如何工作的一些内部视图,并且是深度复制的另一种替代选项。我的函数是否有错误并不重要,因为这样做的目的是展示一种复制问题答案等对象的方法,同时也用它来解释深度复制的核心工作原理。
任何深复制功能的核心都是进行浅复制的方法。如何?简单的。任何深度复制函数仅复制不可变对象的容器。当您深度复制嵌套列表时,您只是复制外部列表,而不是列表内部的可变对象。您只是复制容器。对于班级来说也是如此。当您深度复制一个类时,您会深度复制它的所有可变属性。那么,怎么样?为什么你只需要复制容器,比如列表、字典、元组、迭代器、类和类实例?
这很简单。可变对象实际上无法被复制。它永远无法更改,因此它只是一个值。这意味着您永远不必重复字符串、数字、布尔值或其中任何一个。但是如何复制容器呢?简单的。您只需使用所有值初始化一个新容器。 Deepcopy 依赖于递归。它会复制所有容器,甚至是内部有容器的容器,直到没有容器留下。容器是一个不可变的对象。
一旦你知道了这一点,在没有任何引用的情况下完全复制一个对象就非常容易了。这是一个用于深度复制基本数据类型的函数(不适用于自定义类,但您可以随时添加它)
Python 自己的内置深度复制基于该示例。唯一的区别是它支持其他类型,并且还通过将属性复制到新的重复类中来支持用户类,并且还通过使用备忘录列表或字典引用已经看到的对象来阻止无限递归。这就是制作深拷贝的真正目的。从本质上讲,制作深复制只是制作浅复制。我希望这个答案能为问题增添一些内容。
示例
假设您有以下列表:
[1, 2, 3]
。不可变数字不能重复,但另一层可以。您可以使用列表理解来复制它:[x for x in [1, 2, 3]]
现在,假设您有这个列表:
[[1, 2], [3, 4], [5, 6]]
。这次,您想要创建一个函数,它使用递归来深度复制列表的所有层。而不是以前的列表理解:它对列表使用新的列表:
并且 deepcopy_list 看起来像这样:
那么现在你有一个可以深度复制 strs、bools、floast、ints 的任何列表的函数甚至使用递归列出无限多个层。这就是深度复制。
TLDR:Deepcopy 使用递归来复制对象,并且仅返回与之前相同的不可变对象,因为不可变对象无法复制。但是,它会深度复制可变对象的最内层,直到到达对象的最外可变层。
I wanted to post something a bit different than some of the other answers. Even though this is most likely not the most understandable, or fastest option, it provides a bit of an inside view of how deep copy works, as well as being another alternative option for deep copying. It doesn't really matter if my function has bugs, since the point of this is to show a way to copy objects like the question answers, but also to use this as a point to explain how deepcopy works at its core.
At the core of any deep copy function is way to make a shallow copy. How? Simple. Any deep copy function only duplicates the containers of immutable objects. When you deepcopy a nested list, you are only duplicating the outer lists, not the mutable objects inside of the lists. You are only duplicating the containers. The same works for classes, too. When you deepcopy a class, you deepcopy all of its mutable attributes. So, how? How come you only have to copy the containers, like lists, dicts, tuples, iters, classes, and class instances?
It's simple. A mutable object can't really be duplicated. It can never be changed, so it is only a single value. That means you never have to duplicate strings, numbers, bools, or any of those. But how would you duplicate the containers? Simple. You make just initialize a new container with all of the values. Deepcopy relies on recursion. It duplicates all the containers, even ones with containers inside of them, until no containers are left. A container is an immutable object.
Once you know that, completely duplicating an object without any references is pretty easy. Here's a function for deepcopying basic data-types (wouldn't work for custom classes but you could always add that)
Python's own built-in deepcopy is based around that example. The only difference is it supports other types, and also supports user-classes by duplicating the attributes into a new duplicate class, and also blocks infinite-recursion with a reference to an object it's already seen using a memo list or dictionary. And that's really it for making deep copies. At its core, making a deep copy is just making shallow copies. I hope this answer adds something to the question.
EXAMPLES
Say you have this list:
[1, 2, 3]
. The immutable numbers cannot be duplicated, but the other layer can. You can duplicate it using a list comprehension:[x for x in [1, 2, 3]]
Now, imagine you have this list:
[[1, 2], [3, 4], [5, 6]]
. This time, you want to make a function, which uses recursion to deep copy all layers of the list. Instead of the previous list comprehension:It uses a new one for lists:
And deepcopy_list looks like this:
Then now you have a function which can deepcopy any list of strs, bools, floast, ints and even lists to infinitely many layers using recursion. And there you have it, deepcopying.
TLDR: Deepcopy uses recursion to duplicate objects, and merely returns the same immutable objects as before, as immutable objects cannot be duplicated. However, it deepcopies the most inner layers of mutable objects until it reaches the outermost mutable layer of an object.
请注意,在某些情况下,如果您定义了自己的自定义类并且想要保留属性,那么您应该使用
copy.copy()
或copy.deepcopy()
而不是替代方案,例如在 Python 3 中:输出:
Note that there are some cases where if you have defined your own custom class and you want to keep the attributes then you should use
copy.copy()
orcopy.deepcopy()
rather than the alternatives, for example in Python 3:Outputs:
请记住,在 Python 中,当您这样做时:
List2 并不存储实际列表,而是存储对 list1 的引用。因此,当您对 list1 执行任何操作时,list2 也会发生变化。使用复制模块(非默认,在 pip 上下载)制作列表的原始副本(
copy.copy()
对于简单列表,copy.deepcopy()
对于嵌套的)。这将生成一个不随第一个列表更改的副本。Remember that in Python when you do:
List2 isn't storing the actual list, but a reference to list1. So when you do anything to list1, list2 changes as well. use the copy module (not default, download on pip) to make an original copy of the list(
copy.copy()
for simple lists,copy.deepcopy()
for nested ones). This makes a copy that doesn't change with the first list.通过 id 和 gc 查看内存的一个稍微实用的角度。
A slight practical perspective to look into memory through id and gc.
这是因为,行
new_list = my_list
为变量my_list
分配了一个新引用,即new_list
这类似于下面给出的
C
代码,您应该使用复制模块来创建一个新列表
This is because, the line
new_list = my_list
assigns a new reference to the variablemy_list
which isnew_list
This is similar to the
C
code given below,You should use the copy module to create a new list by
还有另一种方法可以复制之前未列出的列表:添加一个空列表:
l2 = l + []
。我用Python 3.8测试了它:
这不是最好的答案,但它有效。
There is another way of copying a list that was not listed until now: adding an empty list:
l2 = l + []
.I tested it with Python 3.8:
It is not the best answer, but it works.
深度复制选项是唯一适合我的方法:
导致输出:
The deepcopy option is the only method that works for me:
leads to output of:
使用的方法取决于要复制的列表的内容。如果列表包含嵌套的
dicts
,则深度复制是唯一有效的方法,否则答案中列出的大多数方法(切片、循环[for]、复制、扩展、组合或解包)都将有效并以相似的时间执行(循环和深度复制除外,它们的执行情况最差)。脚本
结果
The method to use depends on the contents of the list being copied. If the list contains nested
dicts
than deepcopy is the only method that works, otherwise most of the methods listed in the answers (slice, loop [for], copy, extend, combine, or unpack) will work and execute in similar time (except for loop and deepcopy, which preformed the worst).Script
Results
框架挑战:您的应用程序真的需要复制吗?
我经常看到尝试以某种迭代方式修改列表副本的代码。为了构造一个简单的例子,假设我们有一个不起作用的代码(因为
x
不应该被修改),例如:人们自然会问如何使
y
成为y
的副本code>x,而不是同一列表的名称,以便for
循环执行正确的操作。但这是错误的做法。从功能上讲,我们真正想要做的是创建一个基于原始列表的新列表。
我们不需要先制作副本来做到这一点,而且通常也不应该这样做。
当我们需要将逻辑应用于每个元素时,
最自然的工具就是列表理解。通过这种方式,我们编写了逻辑来告诉我们所需结果中的元素如何与原始元素相关。简洁、优雅、富有表现力;我们避免了在
for
循环中修改y
副本的需要(因为 分配给迭代变量不会影响列表 - 与我们首先想要副本的原因相同!)。对于上面的例子,它看起来像:
列表推导式非常强大;我们还可以使用它们通过带有
if
子句的规则过滤掉元素,并且我们可以链接for
和if
子句(它的工作方式类似于相应的命令式代码,具有相同的子句以相同的顺序;只有最终出现在结果列表中的值被移动到前面,而不是位于“最里面”的部分)。如果计划是在修改副本的同时迭代原始版本以避免出现问题,通常有一种更令人愉快的方法具有过滤列表理解。当我们需要按位置拒绝或插入特定元素时
假设我们有类似的东西
而不是首先将
y
制作为单独的副本以删除我们不想要的部分,我们可以构建一个列表将我们想要的部分放在一起。因此:通过切片处理插入、替换等就留作练习了。只需推理出您希望结果包含哪些子序列即可。一个特殊情况是制作反向副本 - 假设我们根本需要一个新列表(而不仅仅是反向迭代),我们可以直接通过切片来创建它,而不是克隆然后使用
.reverse
。这些方法 - 如列表理解 - 还具有以下优点:它们将所需结果创建为表达式,而不是通过程序就地修改现有对象(以及 返回
无
)。这对于以“流畅”的风格编写代码来说更加方便。Frame challenge: do you actually need to copy, for your application?
I often see code that tries to modify a copy of the list in some iterative fashion. To construct a trivial example, suppose we had non-working (because
x
should not be modified) code like:Naturally people will ask how to make
y
be a copy ofx
, rather than a name for the same list, so that thefor
loop will do the right thing.But this is the wrong approach. Functionally, what we really want to do is make a new list that is based on the original.
We don't need to make a copy first to do that, and we typically shouldn't.
When we need to apply logic to each element
The natural tool for this is a list comprehension. This way, we write the logic that tells us how the elements in the desired result, relate to the original elements. It's simple, elegant and expressive; and we avoid the need for workarounds to modify the
y
copy in afor
loop (since assigning to the iteration variable doesn't affect the list - for the same reason that we wanted the copy in the first place!).For the above example, it looks like:
List comprehensions are quite powerful; we can also use them to filter out elements by a rule with an
if
clause, and we can chainfor
andif
clauses (it works like the corresponding imperative code, with the same clauses in the same order; only the value that will ultimately end up in the result list, is moved to the front instead of being in the "innermost" part). If the plan was to iterate over the original while modifying the copy to avoid problems, there is generally a much more pleasant way to do that with a filtering list comprehension.When we need to reject or insert specific elements by position
Suppose instead that we had something like
Rather than making
y
a separate copy first in order to delete the part we don't want, we can build a list by putting together the parts that we do want. Thus:Handling insertion, replacement etc. by slicing is left as an exercise. Just reason out which subsequences you want the result to contain. A special case of this is making a reversed copy - assuming we need a new list at all (rather than just to iterate in reverse), we can directly create it by slicing, rather than cloning and then using
.reverse
.These approaches - like the list comprehension - also have the advantage that they create the desired result as an expression, rather than by procedurally modifying an existing object in-place (and returning
None
). This is more convenient for writing code in a "fluent" style.对每种复制模式的简短说明:
浅复制构造一个新的复合对象,然后(尽可能)将引用插入到原始对象中 - 创建浅复制:
A 深层复制构造一个新的复合对象,然后递归地将原始对象的副本插入其中 - 创建深层复制:
list()
适用于深层简单列表的副本,例如:但是,对于复杂列表,例如...
...使用
deepcopy()
:Short and simple explanations of each copy mode:
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 - creating a shallow copy:
A deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found in the original - creating a deep copy:
list()
works fine for deep copy of simple lists, like:But, for complex lists like...
...use
deepcopy()
:因为:new_list 只会引用 my_list,并且在 new_list 中所做的更改也会自动在 my_list 中进行> 反之亦然
有两种简单的方法可以复制列表
或
because: new_list will only be a reference to my_list, and changes made in new_list will automatically also be made in my_list and vice versa
There are two easy ways to copy a list
or