python-3.x - 如何使用对对象的内部引用来克隆/深度复制 Python 3.x dict
问题描述
我有以下问题。假设我们有A类和B类:
class A:
def clone(self):
return self.__class__()
class B:
def __init__(self, ref):
self.ref = ref
def clone(self):
return self.__class__(
ref = self.ref
)
我还有一个在dict之后继承的类,称为Holder。
class Holder(dict):
def clone(self):
return self.__class__(
{k: v.clone() for k, v in self.items()}
)
现在我想要的是用我的 clone() 函数以某种方式复制整个 dict (其中已经放入了值),这样引用就不会被弄乱。
这里有一些代码应该澄清我想要的行为:
original = Holder()
original['a'] = A()
original['b'] = B(original['a']) # here we create B object
# with reference to A object
assert original['a'] is original['b'].ref # reference is working
copy = original.clone() # we clone our dict
assert copy['a'] is copy['b'].ref # reference is not working like I want
# copy['b'].ref points to old original['b']
assert original['a'] is not copy['a']
assert original['b'] is not copy['b']
assert original['b'].ref is not copy['b'].ref
以下是下面描述的问题的一些背景:
假设我有一个名为MyClass的类和一个名为MyClassMeta的元类。
我想为MyClassMeta的__prepare__函数提供我自己的字典,该字典将是名为Holder的类的实例。在创建类期间,我会将某些类型的值存储到Holder实例的内部字典中(类似于 EnumMeta 所做的)。由于在类创建期间Holder实例将被填充值,因此MyClass的所有实例都将具有对同一对象的引用。
现在我想要的是每个我的Holder实例都有单独的副本。我以为我可以复制/克隆我的对象,但是当我在同一个字典中添加引用其他对象的对象时出现了问题。
解决方案
在 Python 中克隆自定义数据结构的正确方法是实现__deepcopy__
特殊方法。这就是copy.deepcopy
函数调用的内容。
如文档中所述:
深拷贝操作通常存在两个浅拷贝操作不存在的问题:
- 递归对象(直接或间接包含对其自身的引用的复合对象)可能会导致递归循环。
- 因为深拷贝复制了它可能复制过多的所有内容,例如打算在副本之间共享的数据。【这就是你面临的问题】
该
deepcopy()
函数通过以下方式避免了这些问题:
- 保留在当前复制过程中已复制的对象的“备忘录”字典;和
- 让用户定义的类覆盖复制操作或复制的组件集。
代码
import copy
class A:
def __deepcopy__(self, memo):
return self.__class__()
class B:
def __init__(self, ref):
self.ref = ref
def __deepcopy__(self, memo):
return self.__class__(
ref=copy.deepcopy(self.ref, memo)
)
class Holder(dict):
def __deepcopy__(self, memo):
return self.__class__(
{k: copy.deepcopy(v, memo) for k, v in self.items()}
)
测试
import copy
original = Holder()
original['a'] = A()
original['b'] = B(original['a']) # here we create B object
# with reference to A object
assert original['a'] is original['b'].ref # reference is working
cp = copy.deepcopy(original) # we clone our dict
assert cp['a'] is cp['b'].ref # reference is still working
assert original['a'] is not cp['a']
assert original['b'] is not cp['b']
assert original['b'].ref is not cp['b'].ref
推荐阅读
- sql - 使用 FOR JSON PATH,WITHOUT_ARRAY_WRAPPER 时 SQL 查询不返回值
- javascript - 当单击按钮进入带有角度和 Fullcalendar 的工具提示时,请继续关注 Tippy.js
- html - 无法达到最大输入类型范围
- c++ - C++ Primer 第 5 版:指向成员函数的指针
- flutter - 在 dart 中如何使用路径提供程序将数据保存到文件中
- reactjs - 如何为 Material-UI 导入别名?
- java - 如何正确地将ejb注入接口?
- python - 折叠熊猫数据框中的重叠列
- javascript - 如何将多个元素插入到 JQuery 中的数组中?
- git - oh-my-posh 模块在 git 目录中运行缓慢