如何克隆或复制列表?

aF.*_*aF. 2289 python clone copy list

在Python中克隆或复制列表有哪些选项?

在使用时new_list = my_list,每次都会对new_list更改进行任何修改my_list.为什么是这样?

Fel*_*ing 3044

new_list = my_list,你实际上没有两个列表.分配正义的复制参考列表,而不是实际的列表,所以无论new_listmy_list分配后指向同一个列表.

要实际复制列表,您有各种可能性:

例:

import copy

class Foo(object):
    def __init__(self, val):
         self.val = val

    def __repr__(self):
        return str(self.val)

foo = Foo(1)

a = ['foo', foo]
b = a.copy()
c = a[:]
d = list(a)
e = copy.copy(a)
f = copy.deepcopy(a)

# edit orignal list and instance 
a.append('baz')
foo.val = 5

print('original: %r\n list.copy(): %r\n slice: %r\n list(): %r\n copy: %r\n deepcopy: %r'
      % (a, b, c, d, e, f))
Run Code Online (Sandbox Code Playgroud)

结果:

original: ['foo', 5, 'baz']
list.copy(): ['foo', 5]
slice: ['foo', 5]
list(): ['foo', 5]
copy: ['foo', 5]
deepcopy: ['foo', 1]
Run Code Online (Sandbox Code Playgroud)

  • 正如 @Georgy 在下面的答案中正确指出的那样,对 new_list 值的任何更改也会更改 my_list 中的值。所以实际上 copy.deepcopy() 方法是唯一真正的副本,无需引用原始列表及其值。 (6认同)
  • 另一种可能性是new_list = old_list*1 (4认同)
  • 如果我没有记错的话,在Python 3中也有可能使用newlist = [* mylist]。newlist = list(mylist)可能更清楚。 (3认同)
  • @moojen因为列表本身是可变的,并且新的赋值仅创建引用。例如`x = []; y = x;y.append(1); x` -> `[1]` (3认同)
  • 这些方法中的哪些是浅表复制,哪些是深复制? (2认同)
  • @Eswar:除了最后一个以外的所有对象都进行浅表复制 (2认同)

cry*_*ryo 559

菲利克斯已经提供了一个很好的答案,但我想我会对各种方法进行速度比较:

  1. 10.59秒(105.9us/itn) - copy.deepcopy(old_list)
  2. 10.16秒(101.6us/itn) - Copy()使用deepcopy复制类的纯python 方法
  3. 1.488秒(14.88us/itn) - 纯python Copy()方法不复制类(只有dicts/lists/tuples)
  4. 0.325秒(3.25us/itn) - for item in old_list: new_list.append(item)
  5. 0.217秒(2.17us/itn) - [i for i in old_list](列表理解)
  6. 0.186秒(1.86us/itn) - copy.copy(old_list)
  7. 0.075秒(0.75us/itn) - list(old_list)
  8. 0.053秒(0.53us/itn) - new_list = []; new_list.extend(old_list)
  9. 0.039秒(0.39us/itn) - old_list[:](列表切片)

所以最快的是列表切片.但请注意copy.copy(),list[:]并且list(list),与copy.deepcopy()python版本不同,它不会复制列表中的任何列表,字典和类实例,因此如果原件发生更改,它们也会在复制的列表中更改,反之亦然.

(这是脚本,如果有人有兴趣或想提出任何问题:)

from copy import deepcopy

class old_class:
    def __init__(self):
        self.blah = 'blah'

class new_class(object):
    def __init__(self):
        self.blah = 'blah'

dignore = {str: None, unicode: None, int: None, type(None): None}

def Copy(obj, use_deepcopy=True):
    t = type(obj)

    if t in (list, tuple):
        if t == tuple:
            # Convert to a list if a tuple to 
            # allow assigning to when copying
            is_tuple = True
            obj = list(obj)
        else: 
            # Otherwise just do a quick slice copy
            obj = obj[:]
            is_tuple = False

        # Copy each item recursively
        for x in xrange(len(obj)):
            if type(obj[x]) in dignore:
                continue
            obj[x] = Copy(obj[x], use_deepcopy)

        if is_tuple: 
            # Convert back into a tuple again
            obj = tuple(obj)

    elif t == dict: 
        # Use the fast shallow dict copy() method and copy any 
        # values which aren't immutable (like lists, dicts etc)
        obj = obj.copy()
        for k in obj:
            if type(obj[k]) in dignore:
                continue
            obj[k] = Copy(obj[k], use_deepcopy)

    elif t in dignore: 
        # Numeric or string/unicode? 
        # It's immutable, so ignore it!
        pass 

    elif use_deepcopy: 
        obj = deepcopy(obj)
    return obj

if __name__ == '__main__':
    import copy
    from time import time

    num_times = 100000
    L = [None, 'blah', 1, 543.4532, 
         ['foo'], ('bar',), {'blah': 'blah'},
         old_class(), new_class()]

    t = time()
    for i in xrange(num_times):
        Copy(L)
    print 'Custom Copy:', time()-t

    t = time()
    for i in xrange(num_times):
        Copy(L, use_deepcopy=False)
    print 'Custom Copy Only Copying Lists/Tuples/Dicts (no classes):', time()-t

    t = time()
    for i in xrange(num_times):
        copy.copy(L)
    print 'copy.copy:', time()-t

    t = time()
    for i in xrange(num_times):
        copy.deepcopy(L)
    print 'copy.deepcopy:', time()-t

    t = time()
    for i in xrange(num_times):
        L[:]
    print 'list slicing [:]:', time()-t

    t = time()
    for i in xrange(num_times):
        list(L)
    print 'list(L):', time()-t

    t = time()
    for i in xrange(num_times):
        [i for i in L]
    print 'list expression(L):', time()-t

    t = time()
    for i in xrange(num_times):
        a = []
        a.extend(L)
    print 'list extend:', time()-t

    t = time()
    for i in xrange(num_times):
        a = []
        for y in L:
            a.append(y)
    print 'list append:', time()-t

    t = time()
    for i in xrange(num_times):
        a = []
        a.extend(i for i in L)
    print 'generator expression extend:', time()-t
Run Code Online (Sandbox Code Playgroud)

编辑:在基准测试中添加了新式,旧式类和dicts,并使python版本更快,并添加了更多方法,包括列表表达式和copy.deepcopy(old_list).

  • 由于您是基准测试,因此包含参考点可能会有所帮助.这些数字在2017年使用完全编译的代码的Python 3.6仍然准确吗?我注意到以下答案(/sf/answers/1246721381/)已经对此答案提出质疑. (4认同)
  • 使用`timeit`模块.另外,你不能从像这样的任意微基准做出很多结论. (3认同)
  • 这些数字可能已经过时了。我尝试在我的电脑(甚至相当旧)上运行[脚本的修改版本](https://gist.github.com/wjandrea/04fefb1db77a2eb47739fa4954705013),所有数字都明显较低。值得注意的是,据我统计,列表理解速度快了 4 倍多。 (3认同)

ana*_*nik 140

被告知 Python 3.3+ 添加list.copy()方法,它应该像切片一样快:

newlist = old_list.copy()

  • 是的,根据文档https://docs.python.org/3/library/stdtypes.html#mutable-sequence-types,`s.copy()`创建`s`的浅表副本(与`s相同) [:]`). (4认同)
  • 实际上,目前看来,`python3.8`、`.copy()` 比切片**稍快**。请参阅下面@AaronsHall 的回答。 (4认同)
  • 当然,他们正在致力于[优化常量切片的重复构建](https://bugs.python.org/issue42454),因此在 3.10 中切片可能会再次获胜。但这一切都毫无意义;渐近性能是相同的,并且固定开销相对较小,因此使用哪种方法并不重要。 (3认同)

Aar*_*all 121

在Python中克隆或复制列表有哪些选项?

在Python 3中,可以使用以下方式创建浅表副本:

a_copy = a_list.copy()
Run Code Online (Sandbox Code Playgroud)

在Python 2和3中,您可以获得带有原始片段的浅表副本:

a_copy = a_list[:]
Run Code Online (Sandbox Code Playgroud)

说明

有两种语义方法可以复制列表.浅拷贝创建相同对象的新列表,深拷贝创建包含新等效对象的新列表.

浅清单副本

浅拷贝仅复制列表本身,该列表是对列表中对象的引用的容器.如果包含的对象本身是可变的并且其中一个被更改,则更改将反映在两个列表中.

在Python 2和3中有不同的方法可以做到这一点.Python 2方法也适用于Python 3.

Python 2

在Python 2中,制作列表的浅表副本的惯用方法是使用原始的完整片段:

a_copy = a_list[:]
Run Code Online (Sandbox Code Playgroud)

你也可以通过列表构造函数传递列表来完成同样的事情,

a_copy = list(a_list)
Run Code Online (Sandbox Code Playgroud)

但使用构造函数效率较低:

>>> timeit
>>> l = range(20)
>>> min(timeit.repeat(lambda: l[:]))
0.30504298210144043
>>> min(timeit.repeat(lambda: list(l)))
0.40698814392089844
Run Code Online (Sandbox Code Playgroud)

Python 3

在Python 3中,列表获取list.copy方法:

a_copy = a_list.copy()
Run Code Online (Sandbox Code Playgroud)

在Python 3.5中:

>>> import timeit
>>> l = list(range(20))
>>> min(timeit.repeat(lambda: l[:]))
0.38448613602668047
>>> min(timeit.repeat(lambda: list(l)))
0.6309100328944623
>>> min(timeit.repeat(lambda: l.copy()))
0.38122922903858125
Run Code Online (Sandbox Code Playgroud)

制作另一个指针并没有进行复制

使用new_list = my_list,每次my_list更改时修改new_list.为什么是这样?

my_list只是一个指向内存中实际列表的名称.当你说new_list = my_list你没有制作副本时,你只是添加另一个指向内存中原始列表的名称.当我们制作列表副本时,我们可能会遇到类似的问题.

>>> l = [[], [], []]
>>> l_copy = l[:]
>>> l_copy
[[], [], []]
>>> l_copy[0].append('foo')
>>> l_copy
[['foo'], [], []]
>>> l
[['foo'], [], []]
Run Code Online (Sandbox Code Playgroud)

该列表只是指向内容的指针数组,因此浅复制只复制指针,因此您有两个不同的列表,但它们具有相同的内容.要制作内容的副本,您需要一份深层副本.

深拷贝

为了使列表的深层副本,在Python 2或3时,使用deepcopy了在copy模块:

import copy
a_deep_copy = copy.deepcopy(a_list)
Run Code Online (Sandbox Code Playgroud)

为了演示这如何允许我们创建新的子列表:

>>> import copy
>>> l
[['foo'], [], []]
>>> l_deep_copy = copy.deepcopy(l)
>>> l_deep_copy[0].pop()
'foo'
>>> l_deep_copy
[[], [], []]
>>> l
[['foo'], [], []]
Run Code Online (Sandbox Code Playgroud)

因此我们看到深层复制列表与原始列表完全不同.你可以自己动手 - 但不要.您可能会使用标准库的deepcopy函数创建您不会遇到的错误.

不要用 eval

您可能会将此视为深度复制的一种方式,但不要这样做:

problematic_deep_copy = eval(repr(a_list))
Run Code Online (Sandbox Code Playgroud)
  1. 这很危险,特别是如果你从一个你不信任的来源评估某些东西.
  2. 如果您正在复制的子元素没有可以重现等效元素的表示,那么这是不可靠的.
  3. 它的性能也不太好.

在64位Python 2.7中:

>>> import timeit
>>> import copy
>>> l = range(10)
>>> min(timeit.repeat(lambda: copy.deepcopy(l)))
27.55826997756958
>>> min(timeit.repeat(lambda: eval(repr(l))))
29.04534101486206
Run Code Online (Sandbox Code Playgroud)

在64位Python 3.5上:

>>> import timeit
>>> import copy
>>> l = list(range(10))
>>> min(timeit.repeat(lambda: copy.deepcopy(l)))
16.84255409205798
>>> min(timeit.repeat(lambda: eval(repr(l))))
34.813894678023644
Run Code Online (Sandbox Code Playgroud)

  • 如果列表是二维的,则不需要深拷贝。如果它是一个列表列表,并且这些列表中没有列表,则可以使用 for 循环。目前,我正在使用 `list_copy=[]` `for item in list: list_copy.append(copy(item))` 并且速度要快得多。 (2认同)

jac*_*ack 51

已经有许多答案告诉你如何制作一个正确的副本,但没有一个人说你为什么原来的'副本'失败了.

Python不会将值存储在变量中; 它将名称绑定到对象.您的原始作业采用了所引用的对象并将其my_list绑定new_list.无论您使用哪个名称,仍然只有一个列表,因此在引用它时所做的更改将在引用my_list时保持不变new_list.此问题的其他每个答案都为您提供了创建要绑定的新对象的不同方法new_list.

列表的每个元素都像一个名称,因为每个元素都非唯一地绑定到一个对象.浅拷贝创建一个新列表,其元素绑定到与以前相同的对象.

new_list = list(my_list)  # or my_list[:], but I prefer this syntax
# is simply a shorter way of:
new_list = [element for element in my_list]
Run Code Online (Sandbox Code Playgroud)

要使列表副本更进一步,请复制列表引用的每个对象,并将这些元素副本绑定到新列表.

import copy  
# each element must have __copy__ defined for this...
new_list = [copy.copy(element) for element in my_list]
Run Code Online (Sandbox Code Playgroud)

这还不是一个深层副本,因为列表的每个元素都可以引用其他对象,就像列表绑定到它的元素一样.以递归方式复制列表中的每个元素,然后复制每个元素引用的每个其他对象,依此类推:执行深层复制.

import copy
# each element must have __deepcopy__ defined for this...
new_list = copy.deepcopy(my_list)
Run Code Online (Sandbox Code Playgroud)

有关复制中的边角情况的更多信息,请参阅文档.


Pau*_*jan 34

使用 thing[:]

>>> a = [1,2]
>>> b = a[:]
>>> a += [3]
>>> a
[1, 2, 3]
>>> b
[1, 2]
>>> 
Run Code Online (Sandbox Code Playgroud)


eri*_*sco 32

Python这样做的习惯是 newList = oldList[:]


Riv*_*ver 22

Python 3.6.0计时

以下是使用Python 3.6.0的计时结果.请记住,这些时间是彼此相对的,而不是绝对的.

我坚持只做浅拷贝,并且还添加了Python2中不可能的一些新方法,例如list.copy()(Python3 切片等效)和list unpacking (*new_list, = list):

METHOD                  TIME TAKEN
b = [*a]                2.75180600000021
b = a * 1               3.50215399999990
b = a[:]                3.78278899999986  # Python2 winner (see above)
b = a.copy()            4.20556500000020  # Python3 "slice equivalent" (see above)
b = []; b.extend(a)     4.68069800000012
b = a[0:len(a)]         6.84498999999959
*b, = a                 7.54031799999984
b = list(a)             7.75815899999997
b = [i for i in a]      18.4886440000000
b = copy.copy(a)        18.8254879999999
b = []
for item in a:
  b.append(item)        35.4729199999997
Run Code Online (Sandbox Code Playgroud)

考虑到Python3 new_list = [*list]方法的可读性提高,我们可以看到老赢家仍然名列前茅,但实际上并不是很大.

请注意,这些方法不会为列表以外的任何输入输出等效结果.它们都适用于可切片对象,一些适用于任何可迭代对象,但仅list.copy()适用于任何Python对象.


以下是感兴趣方的测试代码(此处的模板):

import timeit

COUNT = 50000000
print("Array duplicating. Tests run", COUNT, "times")
setup = 'a = [0,1,2,3,4,5,6,7,8,9]; import copy'

print("b = list(a)\t\t", timeit.timeit(stmt='b = list(a)', setup=setup, number=COUNT))
print("b = copy.copy(a)\t", timeit.timeit(stmt='b = copy.copy(a)', setup=setup, number=COUNT))
print("b = a.copy()\t\t", timeit.timeit(stmt='b = a.copy()', setup=setup, number=COUNT))
print("b = a[:]\t\t", timeit.timeit(stmt='b = a[:]', setup=setup, number=COUNT))
print("b = a[0:len(a)]\t\t", timeit.timeit(stmt='b = a[0:len(a)]', setup=setup, number=COUNT))
print("*b, = a\t\t\t", timeit.timeit(stmt='*b, = a', setup=setup, number=COUNT))
print("b = []; b.extend(a)\t", timeit.timeit(stmt='b = []; b.extend(a)', setup=setup, number=COUNT))
print("b = []; for item in a: b.append(item)\t", timeit.timeit(stmt='b = []\nfor item in a:  b.append(item)', setup=setup, number=COUNT))
print("b = [i for i in a]\t", timeit.timeit(stmt='b = [i for i in a]', setup=setup, number=COUNT))
print("b = [*a]\t\t", timeit.timeit(stmt='b = [*a]', setup=setup, number=COUNT))
print("b = a * 1\t\t", timeit.timeit(stmt='b = a * 1', setup=setup, number=COUNT))
Run Code Online (Sandbox Code Playgroud)

  • 可以在 3.8 `b=[*a]` 上确认类似的故事 - 这是一种显而易见的方法;)。 (2认同)

Aad*_*Ura 22

让我们从头开始,探索它有点深:

所以假设你有两个清单:

list_1=['01','98']
list_2=[['01','98']]
Run Code Online (Sandbox Code Playgroud)

我们必须复制这两个列表,现在从第一个列表开始:

所以首先让我们尝试一般的复制方法:

copy=list_1
Run Code Online (Sandbox Code Playgroud)

现在,如果您认为复制复制了list_1那么您可能错了,让我们检查一下:

The id() function shows us that both variables point to the same list object, i.e. they share this object.
Run Code Online (Sandbox Code Playgroud)
print(id(copy))
print(id(list_1))
Run Code Online (Sandbox Code Playgroud)

输出:

4329485320
4329485320
Run Code Online (Sandbox Code Playgroud)

很惊讶?好的,让我们来探索一下:

因此我们知道python不会在变量中存储任何内容,Variables只是引用对象而对象存储该值.这里的对象是,list但我们通过两个不同的变量名创建了对同一个对象的两个引用.所以这两个变量都指向同一个对象:

所以当你做copy=list_1它真正做的事情时:

在此输入图像描述

这里的图像list_1和copy是两个变量名,但两个变量的对象是相同的 list

因此,如果您尝试修改复制列表,那么它也将修改原始列表,因为列表只有一个,无论您是从复制列表还是从原始列表中修改该列表,您都将修改该列表:

copy[0]="modify"

print(copy)
print(list_1)
Run Code Online (Sandbox Code Playgroud)

输出:

['modify', '98']
['modify', '98']
Run Code Online (Sandbox Code Playgroud)

所以它修改了原始列表:

那么解决方案是什么?

方案:

现在让我们转向复制列表的第二种pythonic方法:

copy_1=list_1[:]
Run Code Online (Sandbox Code Playgroud)

现在这个方法解决了我们在第一期中遇到的问题让我们检查一下:

print(id(copy_1))
print(id(list_1))

4338792136
4338791432
Run Code Online (Sandbox Code Playgroud)

因此,我们可以看到我们的两个列表具有不同的id,这意味着两个变量都指向不同的对象,所以这里实际发生的是:

在此输入图像描述

现在让我们尝试修改列表,让我们看看我们是否还面临上一个问题:

copy_1[0]="modify"

print(list_1)
print(copy_1)
Run Code Online (Sandbox Code Playgroud)

输出:

['01', '98']
['modify', '98']
Run Code Online (Sandbox Code Playgroud)

因此,您可以看到它没有修改原始列表,它只修改了复制的列表,所以我们可以使用它.

所以现在我觉得我们已经完成了?等等我们也要复制第二个嵌套列表所以让我们尝试pythonic方式:

copy_2=list_2[:]
Run Code Online (Sandbox Code Playgroud)

所以list_2应该引用另一个对象,它是list_2的副本让我们检查:

print(id((list_2)),id(copy_2))
Run Code Online (Sandbox Code Playgroud)

我们得到输出:

4330403592 4330403528
Run Code Online (Sandbox Code Playgroud)

现在我们可以假设两个列表都指向不同的对象,所以现在让我们尝试修改它,让我们看看它给出了我们想要的东西:

所以当我们尝试:

copy_2[0][1]="modify"

print(list_2,copy_2)
Run Code Online (Sandbox Code Playgroud)

它给我们输出:

[['01', 'modify']] [['01', 'modify']]
Run Code Online (Sandbox Code Playgroud)

现在,我们使用pythonic方式,这有点令人困惑,但我们仍面临同样的问题.

我们理解它:

所以当我们这样做时:

copy_2=list_2[:]
Run Code Online (Sandbox Code Playgroud)

我们实际上只复制外部列表,而不是嵌套列表,所以嵌套列表是两个列表的相同对象,让我们检查:

print(id(copy_2[0]))
print(id(list_2[0]))
Run Code Online (Sandbox Code Playgroud)

输出:

4329485832
4329485832
Run Code Online (Sandbox Code Playgroud)

所以当我们这样做时copy_2=list_2[:] ,会发生什么:

在此输入图像描述

它创建列表的副本,但只有外部列表副本,而不是嵌套列表副本,嵌套列表对于两个变量都是相同的,所以如果您尝试修改嵌套列表,那么它也会修改原始列表,因为嵌套列表对象对于两者都是相同的嵌套列表.

那么解决方案是什么?

解决方案是 deep copy

from copy import deepcopy
deep=deepcopy(list_2)
Run Code Online (Sandbox Code Playgroud)

现在让我们检查一下:

print(id((list_2)),id(deep))
Run Code Online (Sandbox Code Playgroud)

输出:

4322146056 4322148040
Run Code Online (Sandbox Code Playgroud)

两个id都不同,现在让我们检查嵌套列表ID:

print(id(deep[0]))
print(id(list_2[0]))
Run Code Online (Sandbox Code Playgroud)

输出:

4322145992
4322145800
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,两个id都不同,因此我们可以假设两个嵌套列表现在都指向不同的对象.

所以当你做deep=deepcopy(list_2)实际发生的事情时:

在此输入图像描述

因此,两个嵌套列表都指向不同的对象,并且它们现在具有单独的嵌套列表副本.

现在让我们尝试修改嵌套列表,让我们看看它是否解决了以前的问题:

所以如果我们这样做:

deep[0][1]="modify"
print(list_2,deep)
Run Code Online (Sandbox Code Playgroud)

输出:

[['01', '98']] [['01', 'modify']]
Run Code Online (Sandbox Code Playgroud)

因此,您可以看到它没有修改原始嵌套列表,它只修改了复制的列表.

如果您喜欢我的详细答案,请通过提升来告诉我,如果您有任何疑问,请回答这个问题,评论:)


AMR*_*AMR 18

所有其他贡献者给出了很好的答案,当你有一个单一维度(水平)列表时,它可以工作,但是到目前为止提到的方法,只能copy.deepcopy()用于克隆/复制列表,而不是指向嵌套list对象使用多维嵌套列表(列表列表).虽然Felix Kling在他的回答中提到了它,但问题还有一点,可能还有一个使用内置函数的解决方法可能是更快的替代方案deepcopy.

虽然new_list = old_list[:],copy.copy(old_list)'和Py3k old_list.copy()单层次的名单的工作,他们恢复的指向list嵌套内的对象old_listnew_list,并以一个变化list的对象在其他的延续.

编辑:新信息曝光

正如Aaron HallPM 2Ring 指出的那样使用eval()不仅是一个坏主意,它也比它慢得多copy.deepcopy().

这意味着对于多维列表,唯一的选择是copy.deepcopy().说到这一点,它确实不是一个选项,因为当你尝试在中等大小的多维数组上使用它时,性能会越来越好.我尝试timeit使用42x42阵列,这对于生物信息学应用程序来说并不是闻所未闻甚至是那么大,我放弃了等待响应并开始在这篇文章中输入我的编辑.

似乎唯一真正的选择是初始化多个列表并独立地处理它们.如果有人有任何其他建议,对于如何处理多维列表复制,将不胜感激.

正如其他人所说,使用该模块和多维列表可能会 出现严重的性能问题.试图找出一种不使用的复制多维列表的不同方式,(我正在研究一个课程的问题,只允许整个算法运行5秒才能获得信用),我想出了一种方法使用内置函数制作嵌套列表的副本,而不使它们彼此指向或嵌套在它们中的对象.我使用在分配中将旧列表的副本放入新列表而不创建旧列表的链接.它采取以下形式:copycopy.deepcopy copy.deepcopy()listdeepcopynew_list = old_list[:]

__PRE__

基本上它的作用是将copy.copy(old_list)'字符串表示为一个字符串,然后计算字符串,就像它是字符串所代表的对象一样.通过这样做,不会链接到原始old_list.copy()对象.list创建一个新对象,每个变量指向它自己的独立对象.以下是使用二维嵌套列表的示例.

__PRE__

如果您然后检查每个列表的内容,例如4乘3列表,Python将返回

__PRE__

虽然这可能不是规范或语法上正确的方法,但它似乎运作良好.我没有测试过的表现,但我要猜测old_listnew_list将有较少的开销比运行list的意愿.

  • 这并不总是有效,因为不能保证`repr()`返回的字符串足以重新创建对象.另外,`eval()`是最后的工具; 详情请见SO退伍军人Ned Batchelder [Eval真的很危险](http://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html).因此,当你提倡使用`eval()时,你_really_应该提到它可能是危险的. (4认同)

SCB*_*SCB 11

让我感到惊讶的是,这还没有被提及,所以为了完整起见......

您可以使用"splat operator"执行列表解包:*这也将复制列表中的元素.

old_list = [1, 2, 3]

new_list = [*old_list]

new_list.append(4)
old_list == [1, 2, 3]
new_list == [1, 2, 3, 4]
Run Code Online (Sandbox Code Playgroud)

这种方法的明显缺点是它只能在Python 3.5+中使用.

虽然时间明智,但这似乎比其他常用方法表现更好.

x = [random.random() for _ in range(1000)]

%timeit a = list(x)
%timeit a = x.copy()
%timeit a = x[:]

%timeit a = [*x]

#: 2.47 µs ± 38.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
#: 2.47 µs ± 54.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
#: 2.39 µs ± 58.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

#: 2.22 µs ± 43.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
Run Code Online (Sandbox Code Playgroud)

  • @not2qubit 您的意思是附加到或编辑新列表的元素。在示例中,`old_list` 和 `new_list` 是两个不同的列表,编辑一个不会改变另一个(除非你直接改变元素本身(例如列表的列表),这些方法都不是深拷贝)。 (2认同)

Rav*_*kar 6

new_list = my_list[:]
Run Code Online (Sandbox Code Playgroud)

new_list = my_list

试着理解这一点。假设my_list位于 X 位置的堆内存中,即my_list指向 X。现在通过赋值new_list = my_listnew_list指向 X。这被称为浅拷贝

现在,如果您赋值new_list = my_list[:],您只需将my_list 的每个对象复制到new_list。这称为深拷贝

其他的你可以做到这一点的方式是:

  • new_list = list(old_list)
  • import copy new_list = copy.deepcopy(old_list)


jai*_*ish 6

已经给出的答案中缺少一种独立于python版本的非常简单的方法,您可以在大多数时间使用它(至少我可以这样做):

new_list = my_list * 1       #Solution 1 when you are not using nested lists
Run Code Online (Sandbox Code Playgroud)

但是,如果my_list包含其他容器(例如,嵌套列表),则必须使用Deepcopy,如上面复制库中答案中所建议的那样。例如:

import copy
new_list = copy.deepcopy(my_list)   #Solution 2 when you are using nested lists
Run Code Online (Sandbox Code Playgroud)

奖励:如果您不想复制元素,请使用(也称为浅表复制):

new_list = my_list[:]
Run Code Online (Sandbox Code Playgroud)

让我们了解解决方案1和解决方案2之间的区别

>>> a = range(5)
>>> b = a*1
>>> a,b
([0, 1, 2, 3, 4], [0, 1, 2, 3, 4])
>>> a[2] = 55 
>>> a,b
([0, 1, 55, 3, 4], [0, 1, 2, 3, 4])
Run Code Online (Sandbox Code Playgroud)

如您所见,当我们不使用嵌套列表时,解决方案1可以完美地工作。让我们检查一下将解决方案1应用于嵌套列表时会发生什么。

>>> from copy import deepcopy
>>> a = [range(i,i+4) for i in range(3)]
>>> a
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
>>> b = a*1
>>> c = deepcopy(a)
>>> for i in (a, b, c): print i   
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
>>> a[2].append('99')
>>> for i in (a, b, c): print i   
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5, 99]]
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5, 99]]   #Solution#1 didn't work in nested list
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]       #Solution #2 - DeepCopy worked in nested list
Run Code Online (Sandbox Code Playgroud)


Chr*_*nds 6

请注意,在某些情况下,如果您定义了自己的自定义类并且想要保留属性,则应使用copy.copy()copy.deepcopy()而不是替代方法,例如在Python 3中:

import copy

class MyList(list):
    pass

lst = MyList([1,2,3])

lst.name = 'custom list'

d = {
'original': lst,
'slicecopy' : lst[:],
'lstcopy' : lst.copy(),
'copycopy': copy.copy(lst),
'deepcopy': copy.deepcopy(lst)
}


for k,v in d.items():
    print('lst: {}'.format(k), end=', ')
    try:
        name = v.name
    except AttributeError:
        name = 'NA'
    print('name: {}'.format(name))
Run Code Online (Sandbox Code Playgroud)

输出:

lst: original, name: custom list
lst: slicecopy, name: NA
lst: lstcopy, name: NA
lst: copycopy, name: custom list
lst: deepcopy, name: custom list
Run Code Online (Sandbox Code Playgroud)


Cor*_*man 5

我想发布一些与其他一些答案有所不同的内容。尽管这很可能不是最容易理解或最快的选项,但它提供了一些关于深复制如何工作的内部视图,并且是深复制的另一种选择。我的函数是否有错误并不重要,因为这样做的目的是展示一种复制问题答案之类的对象的方法,而且还以此作为解释 deepcopy 如何在其核心工作的一个点。

任何深拷贝功能的核心都是制作浅拷贝的方法。如何?简单的。任何深度复制功能只复制不可变对象的容器。当您深度复制嵌套列表时,您只会复制外部列表,而不是列表内的可变对象。您只是在复制容器。这同样适用于类。当你深度复制一个类时,你就深度复制了它的所有可变属性。又怎样?为什么你只需要复制容器,比如列表、字典、元组、迭代器、类和类实例?

这很简单。一个可变对象真的不能被复制。它永远无法更改,因此它只是一个值。这意味着您永远不必复制字符串、数字、布尔值或任何这些。但是你将如何复制容器?简单的。您只需使用所有值初始化一个新容器。Deepcopy 依赖于递归。它复制所有的容器,甚至是里面有容器的容器,直到没有容器为止。容器是一个不可变的对象。

一旦你知道了这一点,完全复制一个没有任何引用的对象就很容易了。这是一个用于深度复制基本数据类型的函数(不适用于自定义类,但您可以随时添加)

def deepcopy(x):
  immutables = (str, int, bool, float)
  mutables = (list, dict, tuple)
  if isinstance(x, immutables):
    return x
  elif isinstance(x, mutables):
    if isinstance(x, tuple):
      return tuple(deepcopy(list(x)))
    elif isinstance(x, list):
      return [deepcopy(y) for y in x]
    elif isinstance(x, dict):
      values = [deepcopy(y) for y in list(x.values())]
      keys = list(x.keys())
      return dict(zip(keys, values))
Run Code Online (Sandbox Code Playgroud)

Python 自己的内置 deepcopy 就是基于该示例。唯一的区别是它支持其他类型,并且还通过将属性复制到新的重复类中来支持用户类,并且还通过引用它已经使用备忘录列表或字典看到的对象来阻止无限递归。这就是制作深拷贝的真正意义所在。从本质上讲,制作深拷贝只是制作浅拷贝。我希望这个答案能为问题增加一些东西。

例子

假设你有这样的名单:[1, 2, 3]。不可变数字不能复制,但其他层可以。您可以使用列表理解来复制它:[x for x in [1, 2, 3]]

现在,想象你有这样的名单:[[1, 2], [3, 4], [5, 6]]。这一次,你想要做一个函数,它使用递归来深度复制列表的所有层。而不是之前的列表理解:

[x for x in _list]
Run Code Online (Sandbox Code Playgroud)

它使用一个新的列表:

[deepcopy_list(x) for x in _list]
Run Code Online (Sandbox Code Playgroud)

deepcopy_list看起来是这样的:

def deepcopy_list(x):
  if isinstance(x, (str, bool, float, int)):
    return x
  else:
    return [deepcopy_list(y) for y in x]
Run Code Online (Sandbox Code Playgroud)

然后现在你有了一个函数,它可以使用递归将任何strs、bools、floast、ints甚至列表的列表深度复制到无限多个层。有了它,深度复制。

TLDR : Deepcopy 使用递归来复制对象,并且只返回与以前相同的不可变对象,因为不可变对象不能被复制。然而,它会深度复制可变对象的最内层,直到它到达对象的最外层可变层。


小智 5

请记住,在 Python 中执行以下操作时:

    list1 = ['apples','bananas','pineapples']
    list2 = list1
Run Code Online (Sandbox Code Playgroud)

List2 不是存储实际列表,而是对 list1 的引用。因此,当您对 list1 执行任何操作时,list2 也会发生变化。使用复制模块(非默认,在 pip 上下载)制作列表的原始副本(copy.copy()对于简单列表,copy.deepcopy()对于嵌套列表)。这会生成一个不会随第一个列表而改变的副本。


归档时间:

查看次数:

1492296 次

最近记录:

5 年,9 月 前