如何制作一个完全不共享的复杂列表副本?(深复制不够)
看看这段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]]]
注意,当你修改了
问题:有没有什么办法可以让
(抱歉上面的问题表述得不好。Python高手们请随意修改问题或标签,以更好地表达这个疑问。)
5 个回答
根据你的情况,你可能想要对这个列表进行一个深拷贝。
当你想要一个副本时,你需要明确地去复制。那种神秘的 [:]
“切片全部”的写法虽然是常见用法,但我更喜欢直接调用 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
的结构,包括对同一子列表的多个引用!:-)
如果你想把一个已经存在的列表(里面还有列表)转换成一个没有任何共享部分的新列表,你可以使用递归的方法来复制这个列表。
单纯使用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数组、字典或者自定义类),你可能需要对这个方法进行一些调整。