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[

请看一下以下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[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())