python字符串拷贝 - 如何克隆或复制列表?




python字典复制 (12)

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

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


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

在Python 3中,可以使用以下内容进行浅度复制:

a_copy = a_list.copy()

在Python 2和Python 3中,你可以得到一个完整的原始片断的浅拷贝:

a_copy = a_list[:]

说明

有两种语义方法来复制列表。 浅拷贝创建相同对象的新列表,深拷贝创建包含新等价对象的新列表。

浅列表复制

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

在Python 2和Python 3中有不同的方法。Python 2的方式也可以在Python 3中使用。

Python 2

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

a_copy = a_list[:]

你也可以通过列表构造函数完成相同的事情,

a_copy = list(a_list)

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

>>> timeit
>>> l = range(20)
>>> min(timeit.repeat(lambda: l[:]))
0.30504298210144043
>>> min(timeit.repeat(lambda: list(l)))
0.40698814392089844

Python 3

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

a_copy = a_list.copy()

在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

制作另一个指针不会创建副本

每次my_list更改时,使用new_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'], [], []]

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

深拷贝

在Python 2或3中创建列表深层副本,请在copy模块中使用deepcopy

import copy
a_deep_copy = copy.deepcopy(a_list)

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

>>> import copy
>>> l
[['foo'], [], []]
>>> l_deep_copy = copy.deepcopy(l)
>>> l_deep_copy[0].pop()
'foo'
>>> l_deep_copy
[[], [], []]
>>> l
[['foo'], [], []]

所以我们看到深度复制列表与原始列表完全不同。 你可以推出你自己的功能 - 但不要。 通过使用标准库的deepcopy功能,您可能会创建一些本来不会有的bug。

不要使用eval

您可能会看到这被用作深度复制的方式,但不要这样做:

problematic_deep_copy = eval(repr(a_list))
  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

在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

让我们从头开始,探索一下它:

所以假设你有两个列表:

list_1=['01','98']
list_2=[['01','98']]

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

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

copy=list_1

现在,如果你想复制copy_1,那么你可能是错的,让我们来看看它:

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

输出:

4329485320
4329485320

惊讶吗? 好吧,我们来探讨一下:

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

所以当你做copy=list_1 ,它实际上做了什么:

这里在图像列表1和副本是两个变量名称,但对象是两个变量是相同的list

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

copy[0]="modify"

print(copy)
print(list_1)

输出:

['modify', '98']
['modify', '98']

所以它修改了原来的列表:

那么解决方案是什么?

解答:

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

copy_1=list_1[:]

现在这个方法解决了我们在第一个问题中遇到的问题让我们来看看它:

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

4338792136
4338791432

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

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

copy_1[0]="modify"

print(list_1)
print(copy_1)

输出:

['01', '98']
['modify', '98']

因此,您可以看到它不会修改原始列表,它只会修改复制的列表,所以我们对它仍然满意。

那么现在我认为我们完成了? 等待我们必须复制第二个嵌套列表,所以让我们尝试pythonic方式:

copy_2=list_2[:]

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

print(id((list_2)),id(copy_2))

我们得到的结果是:

4330403592 4330403528

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

所以当我们尝试时:

copy_2[0][1]="modify"

print(list_2,copy_2)

它给了我们输出:

[['01', 'modify']] [['01', 'modify']]

现在,我们用pythonic的方式并没有多少困惑,我们仍然面临同样的问题。

让我们了解它:

所以当我们这样做时:

copy_2=list_2[:]

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

print(id(copy_2[0]))
print(id(list_2[0]))

输出:

4329485832
4329485832

所以实际上当我们做copy_2=list_2[:]时会发生什么:

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

那么解决方案是什么?

解决方案是deep copy

from copy import deepcopy
deep=deepcopy(list_2)

所以现在让我们来看看它:

print(id((list_2)),id(deep))

输出:

4322146056 4322148040

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

print(id(deep[0]))
print(id(list_2[0]))

输出:

4322145992
4322145800

正如你可以看到这两个ID是不同的,所以我们可以假设两个嵌套列表现在都指向不同的对象。

所以当你做deep=deepcopy(list_2)实际上发生了什么:

因此,两个嵌套列表都指向不同的对象,现在他们有单独的嵌套列表副本。

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

所以如果我们这样做:

deep[0][1]="modify"
print(list_2,deep)

输出:

[['01', '98']] [['01', 'modify']]

因此,您可以看到它不会修改原始嵌套列表,它只会修改复制的列表。

如果你喜欢我的详细答案,请通过提高它来让我知道,如果你有任何疑问了解这个答案,请评论:)


Python的成语是newList = oldList[:]


new_list = list(old_list)


不知道这是否仍然是实际的,但字典中也有相同的行为。 看看这个例子。

a = {'par' : [1,21,3], 'sar' : [5,6,8]}
b = a
c = a.copy()
a['har'] = [1,2,3]

a
Out[14]: {'har': [1, 2, 3], 'par': [1, 21, 3], 'sar': [5, 6, 8]}

b
Out[15]: {'har': [1, 2, 3], 'par': [1, 21, 3], 'sar': [5, 6, 8]}

c
Out[16]: {'par': [1, 21, 3], 'sar': [5, 6, 8]}

与其他具有变量和值的语言不同,Python具有名称和对象

这个说法:

a = [1,2,3]

意味着给列表(对象)一个名字a ,并且,这个:

b = a

只是给同一个对象a新的名字b ,所以每当你对a做某事时,对象都会改变,因此b就会改变。

真正复制a的唯一方法就是创建一个新的对象,就像已经说过的其他答案一样。

你可以here看到更多。


你可以在list()函数中使用bulit:

newlist=list(oldlist)

我认为这段代码会帮助你。


使用thing[:]

>>> a = [1,2]
>>> b = a[:]
>>> a += [3]
>>> a
[1, 2, 3]
>>> b
[1, 2]
>>> 

我听说Python 3.3+ 增加了list.copy()方法,它应该像切片一样快:

newlist = old_list.copy()


所有其他的贡献者都给出了很好的答案,当你有一个单一维度( copy.deepcopy() )的列表,但是到目前为止所提到的方法,只有copy.deepcopy()可以克隆/复制一个列表,而不是指向当您使用多维嵌套列表(列表列表)时,嵌套list对象。 尽管在他的回答中提到了这个问题,但问题还有一点,可能还有一种解决方法是使用内置插件,这可能是一种更快速的deepcopy替代方案。

虽然new_list = old_list[:]copy.copy(old_list)'和Py3k old_list.copy()适用于单层列表,但它们还是会指向嵌套在old_listnew_list中的list对象,并更改为一个的list对象在其他list中永久存在。

编辑:新的信息曝光

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

这意味着对于多维列表,唯一的选择是copy.deepcopy() 。 这就是说,当你尝试在中等大小的多维阵列上使用它时,它并不是一种选择,因为性能会朝南。 我尝试使用42x42阵列进行计时,这对于生物信息学应用程序来说并非前所未闻,甚至是那么大,我放弃了等待响应,并开始在本文中输入编辑。

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

正如其他人所说,使用copy模块和copy.deepcopy 用于多维列表 可能会有 显着的性能问题。 试图找出复制多维列表而不使用deepcopy拷贝的另一种方式(我正在研究一个课程的问题,为了获得学分只允许整个算法运行5秒),我想出了一个方法使用内置函数来创建嵌套列表的副本,而不需要将它们指向彼此或嵌套在list中的list对象。 我在赋值中使用了eval()repr() ,使旧列表的副本进入新列表,而不创建指向旧列表的链接。 它采取以下形式:

new_list = eval(repr(old_list))

基本上,它所做的是将old_list表示为一个字符串,然后将该字符串评估为该字符串表示的对象。 通过这样做,不会链接到原始list对象。 新的list对象被创建并且每个变量指向它自己的独立对象。 这里是一个使用2维嵌套列表的例子。

old_list = [[0 for j in range(y)] for i in range(x)] # initialize (x,y) nested list

# assign a copy of old_list to new list without them pointing to the same list object
new_list = eval(repr(old_list)) 

# make a change to new_list 
for j in range(y):
    for i in range(x):
    new_list[i][j] += 1

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

>>> new_list

[[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]]

>>> old_list

[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]

虽然这可能不是规范或语法正确的方法,但它似乎运作良好。 我没有测试过性能,但我会猜测eval()rep()运行开销比deepcopy要少。


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

  1. 10.59秒(105.9us / itn) - copy.deepcopy(old_list)
  2. 10.16秒(101.6us / itn) - 使用deepcopy复制类的纯python Copy()方法
  3. 1.488秒(14.88us / itn) - 纯python Copy()方法不复制类(只有字符串/列表/元组)
  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) 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

编辑 :添加新样式,旧式的类和字符串的基准,并使python版本更快,并添加了一些更多的方法,包括列表表达式和extend()


new_list = my_list[:]

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 = list(old_list)
  • import copy new_list = copy.deepcopy(old_list)






clone