如何制作复杂列表的完全非共享副本? (深层复制还不够)
题
看看这个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大师请随时修改问题或标签以更好地表达此查询。)
解决方案
要将现有的列表列表转换为不共享任何内容的列表,您可以递归复制列表。
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数组,dicts或用户类),则可能需要更改它。
其他提示
当您需要副本时,您明确地制作副本 - 密码 [:]
" slice it all"形式是惯用的,但我最喜欢的是显式调用 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的拓扑结构,包括对同一个子列表的多个引用! : - )
根据您的具体情况,您可能希望违反深层复制列表。
使用 [:]
:
>>> a = [1, 2]
>>> b = a[:]
>>> b.append(9)
>>> a
[1, 2]
另外,使用 copy
或 deepcopy
:
>>> import copy
>>> a = [1, 2]
>>> b = copy.copy(a)
>>> b.append(9)
>>> a
[1, 2]
copy
适用于列表以外的对象。对于列表,它与 a [:]
具有相同的效果。 deepcopy
尝试递归地复制嵌套元素,因此更加“彻底”。 copy
的操作。
要查看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]]]