要删除列表中的共享引用?

时间:2019-01-31 18:39:35

标签: python reference nested-lists deep-copy

好,让我用一个简单的例子来解释这个问题:

l = [[0]]*3       # makes the array [[0], [0], [0]]
l[0][0] = 42      # l becomes [[42], [42], [42]]
from copy import deepcopy
m = deepcopy(l)   # m becomes [[42], [42], [42]]
m[0][0] = 2       # m becomes [[2], [2], [2]]

这是一个基本的共享参考问题。除非通常发生这种问题,否则deepcopy是我们的朋友。 目前,我提出这个解决我deepcopy出卖问题:

l = [[0]]*3       # makes the array [[0], [0], [0]]
import JSON
m = JSON.loads(JSON.dumps(l)) # m becomes [[0], [0], [0]] with no self references

我正在寻找一种效率低下,愚蠢的方式来处理自我共享引用。

当然,我不会故意创建这样的数组,但是我需要处理有人给我的代码一个数组的情况。在大型阵列上运行我的“解决方案”是缓慢的,而且我有嵌套数组的多层次,我不能做一个字符串,这个大的活物。

7 个答案:

答案 0 :(得分:10)

这是一种适用于列表,字典和不可变值的任意组合的方法。

def very_deep_copy(obj):
    if isinstance(obj, list):
        return [very_deep_copy(item) for item in obj]
    elif isinstance(obj, dict):
        return {k: very_deep_copy(v) for k,v in obj.items()}
    else:
        return obj

l = [[0]]*3 
m = very_deep_copy(l)
m[0][0] = 2
print(m)

结果:

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

答案 1 :(得分:7)

我将挑战这样一个假设,即正确的做法是复制共享对象。你这么说

  

当然,我不会故意创建这样的数组,但是我需要处理有人给我的代码一个数组的情况。

但是,如果有人向您传递包含意外对象共享的输入,则他们的代码存在错误。如果您的代码注意到该错误,则您的代码应通过抛出异常来告知他们,以帮助他们修复其错误。

大多数代码只是假设输入没有任何不需要的对象共享。如果您仍然想检测到它,则手动遍历可能是最好的选择,尤其是因为您的输入应该是JSON可序列化的:

def detect_duplicate_references(data):
    _detect_duplicate_references(data, set())

def _detect_duplicate_references(data, memo):
    if isinstance(data, (dict, list)):
        if id(data) in memo:
            raise ValueError(f'multiple references to object {data} detected in input')
        memo.add(id(data))
    if isinstance(data, list):
        for obj in data:
            _detect_duplicate_references(obj, memo)
    if isinstance(data, dict):
        for obj in data.values():
            _detect_duplicate_references(obj, memo)

答案 2 :(得分:2)

l = [[0] for _ in range(3)]
l[0][0] = 2
print(l)

打印:

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

此外,顺便说一句,在代码中 deepcopy()给出了它的输出,因为您传入了一个列表,该列表已经具有共享相同引用的元素。

from copy import deepcopy
m = deepcopy(l)
m[0][0] = 3
print(l) # prints [[2], [0], [0]]
print(m) # prints [[3], [0], [0]]

您可以在这里看到它确实达到了我们的预期

答案 3 :(得分:1)

假定将仅列出结构类型,这应适用于任意深度和复杂性的列表:

def customcopy(l):
    return [customcopy(e) if type(e) is list else e for e in l]

l = [[0]]*3
x = customcopy(l)
x[0][0] = 3

>>> x
[[3], [0], [0]]

答案 4 :(得分:0)

不确定它是否有效,但是您可以尝试:

l = [deepcopy(elt) for elt in l]

答案 5 :(得分:0)

对于单层深层副本,您只需检查引用即可。对于更深的副本,只需递归执行即可。

from copy import deepcopy

def copy_value(l):
    l = list(l)
    new_list = []
    while l:
        item = l.pop(0)
        if item not in new_list:
            new_list.append(item)
        else:
            new_list.append(deepcopy(item))
    return new_list

l = [[0]]*3 
m = copy_value(l)
m[0][0] = 2
print(m)

打印

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

答案 6 :(得分:0)

列表理解的另一种方法:

def super_deep_copy(l):
   return [i.copy() for i in l]
l = [[0]]*3
l = super_deep_copy(l)
l[0][0] = 2

现在:

print(l)

是:

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