在深度复制期间防止参考重用

Preventing reference re-use during deepcopy

考虑以下示例:

from copy import deepcopy

item = [0]
orig = [item, item]
copy = deepcopy(orig)

orig[0][0] = 1
print(f"{orig=} {copy=}")

copy[0][0] = 2
print(f"{orig=} {copy=}")

第一个 print 输出我所期望的,因为相同的引用在列表中重复。

orig=[[1], [1]] copy=[[0], [0]]

然而,第二个print让我感到惊讶。

orig=[[1], [1]] copy=[[2], [2]]

我原以为 deepcopy 最终会在 copy 列表中包含两个独立的引用。相反,它维护复制的单个列表引用的 属性。我猜 the docs:

的这一部分提到了这一点

A deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found in the original.

我看到 deepcopy 函数有一个 memo 参数。有没有什么有趣的事情可以用这个参数来防止重复引用,这样最终的输出就会变成:

orig=[[1], [1]] copy=[[2], [0]]

哈,这似乎比我想象的要容易,但我 90% 确定这是邪恶的。如果有人发布更好的答案或解释为什么这很糟糕,我会删除它。

实施一个 dict,仅 假装 设置一个值。然后示例 returns 相同引用的单独副本。

class NoMemo(dict):
    def __setitem__(self, key, value):
        return value
...
copy = deepcopy(orig, memo=NoMemo())
...

打印:

orig=[[1], [1]] copy=[[0], [0]]
orig=[[1], [1]] copy=[[2], [0]]

如果您的全部目的是复制可能来自 JSON 的数据,即 list、dict、string、numbers、bool,那么您可以简单地实现自己的函数:

def copy_jsonlike(data):
    if isinstance(data, list):
        return [copy_jsonlike(x) for x in data]
    elif isinstance(data, dict):
        return {k: copy_jsonlike(v) for k,v in data.items()}
    else:
        return data

它的额外好处是可能比 copy.deepcopy

更快

或者,您的原始解决方案 json.loads(json.dumps(data)) 也不错。