Python 如何制作复杂列表的完全非共享副本?(深度复制是不够的)
请看一下以下Python代码:Python 如何制作复杂列表的完全非共享副本?(深度复制是不够的),python,list,copy,Python,List,Copy,请看一下以下Python代码: a = [1, 2, 3] b = [4, 5, 6] c = [[a, b], [b, a]] # [[[1, 2, 3], [4, 5, 6]], [[4, 5, 6], [1, 2, 3]]] c[0][0].append(99) # [[[1, 2, 3, 99], [4, 5, 6]], [[4, 5, 6], [1, 2, 3, 99]]] 请注意,修改c的一个元素会在任何地方修改该元素。也就是说,如果99被附加到c[0][0],它也被附加到c[
a = [1, 2, 3]
b = [4, 5, 6]
c = [[a, b], [b, a]] # [[[1, 2, 3], [4, 5, 6]], [[4, 5, 6], [1, 2, 3]]]
c[0][0].append(99) # [[[1, 2, 3, 99], [4, 5, 6]], [[4, 5, 6], [1, 2, 3, 99]]]
请注意,修改c
的一个元素会在任何地方修改该元素。也就是说,如果99
被附加到c[0][0]
,它也被附加到c[1][1]
。我猜这是因为Python巧妙地为c[0][0]
和c[1][1]
引用了相同的对象。(也就是说,它们的id()是相同的。)
问题:是否可以对c
执行一些操作,以便安全地在本地修改其列表元素?上面只是一个例子,我真正的问题有一个更复杂的列表,但有一个类似的问题
(很抱歉上面的问题格式不正确。Python大师请随意修改问题或标记以更好地表达此查询。)使用[:]
:
>>> a = [1, 2]
>>> b = a[:]
>>> b.append(9)
>>> a
[1, 2]
或者,使用:
copy
适用于列表以外的对象。对于列表,其效果与a[:]
相同deepcopy
尝试递归地复制嵌套元素,因此是一种更“彻底”的操作,copy
根据您的情况,您可能希望针对此列表的一部分进行操作。当您需要副本时,您显式地复制一个副本-神秘的[:]
将其全部切片”形式是惯用的,但我最喜欢的是更具可读性的方法,即显式调用list
如果c
是以错误的方式构建的(使用引用而不是对您希望能够独立修改的列表的浅拷贝),最好的方法是修复它的构建方式(为什么要构建错误,然后努力修复它?!),但如果这超出了您的控制范围,则可以撤消损坏--只需循环c
(如果需要,递归地),使用索引,将相关子列表重新分配给它们的副本。例如,如果您确实知道c
的结构是您所指出的两级结构,则可以不递归地保存自己:
def fixthewronglymadelist(c):
for topsublist in c:
for i, L in enumerate(topsublist):
topsublist[i] = list(L)
尽管其他答案表明,
copy.deepcopy
很难达到这个特殊的目的,如果你得到的只是错误的c
:只做copy。deepcopy(c)
会仔细复制c的拓扑结构,包括对同一个子列表的多个引用!:-)要了解Stephan在工作中的建议,请比较以下两种输出:
a = [1, 2, 3]
b = [4, 5, 6]
c = [[a, b], [b, a]]
c[0][0].append(99)
print c
print "-------------------"
a = [1, 2, 3]
b = [4, 5, 6]
c = [[a[:], b[:]], [b[:], a[:]]]
c[0][0].append(99)
print c
结果如下:
[[[1, 2, 3, 99], [4, 5, 6]], [[4, 5, 6], [1, 2, 3, 99]]]
-------------------
[[[1, 2, 3, 99], [4, 5, 6]], [[4, 5, 6], [1, 2, 3]]]
要将现有列表转换为不共享任何内容的列表,可以递归复制该列表
deepcopy
是不够的,因为它将按原样复制结构,保留内部引用作为引用,而不是副本
def unshared_copy(inList):
if isinstance(inList, list):
return list( map(unshared_copy, inList) )
return inList
alist = unshared_copy(your_function_returning_lists())
请注意,这假定数据作为列表列表(任意嵌套)返回。
如果容器的类型不同(例如numpy数组、dict或用户类),您可能需要对此进行更改。在半小时内,您可能需要学习Python的数据模型:Triptych:谢谢您的建议。迈克尔:请记住,在这种情况下,我从一个我无法控制的函数中得到c。如果你是c,里面可能有很多这样的列表,它们引用同一个对象,你会如何处理它?谢谢你的建议。但是,浅拷贝和深拷贝都不能解决我的实际问题。我不得不求助于Brian的解决方案。在Python2.7中,在命令开始工作之前,必须从导入副本开始,这是文档没有告诉您的,新手可能没有意识到的(可能认为这是内置的)。在测试中,这个答案解决了一个相当复杂的类似问题,其中列表看起来比这个问题中给出的例子更复杂。这意味着应该在可行的地方尝试使用深度复制,而在不可行的时候应该使用公认的答案。简单易读总是更好的,但只有当它起作用的时候。谢谢Alex。事实上,我的实际列表更复杂(正在构建中),深度复制并没有解决问题。我使用了一个递归的非共享副本(Brian的答案)来解决这个问题(因为列表是3级的)。谢谢Stephan202。您的解决方案仅适用于非常简单的列表及其构造。我的实际列表在构造上要复杂得多,最终解决我问题的是Brian给出的列表递归非共享副本的答案。谢谢Brian!你关于深度复制不起作用的直觉是正确的!你的回答解决了我的实际问题。我还修改了Python3的代码,其中map()返回一个映射对象,而不是列表。
def unshared_copy(inList):
if isinstance(inList, list):
return list( map(unshared_copy, inList) )
return inList
alist = unshared_copy(your_function_returning_lists())