如何覆盖Python对象的复制/深度复制操作?
我理解复制模块中的如何覆盖Python对象的复制/深度复制操作?,python,python-internals,Python,Python Internals,我理解复制模块中的copy与deepcopy之间的区别。我曾经成功地使用过copy.copy和copy.deepcopy,但这是我第一次真正地重载\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu和方法。我已经在谷歌上搜索了一下内置的Python模块,寻找\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
copy
与deepcopy
之间的区别。我曾经成功地使用过copy.copy
和copy.deepcopy
,但这是我第一次真正地重载\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu和方法。我已经在谷歌上搜索了一下内置的Python模块,寻找\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
以下是我的设想:
我有一个配置对象。首先,我将用一组默认值实例化一个配置对象。此配置将移交给多个其他对象(以确保所有对象以相同的配置启动)。然而,一旦用户交互开始,每个对象都需要独立地调整其配置,而不影响彼此的配置(这对我来说意味着我需要制作初始配置的deepcopy以供分发)
下面是一个示例对象:
class ChartConfig(object):
def __init__(self):
#Drawing properties (Booleans/strings)
self.antialiased = None
self.plot_style = None
self.plot_title = None
self.autoscale = None
#X axis properties (strings/ints)
self.xaxis_title = None
self.xaxis_tick_rotation = None
self.xaxis_tick_align = None
#Y axis properties (strings/ints)
self.yaxis_title = None
self.yaxis_tick_rotation = None
self.yaxis_tick_align = None
#A list of non-primitive objects
self.trace_configs = []
def __copy__(self):
pass
def __deepcopy__(self, memo):
pass
在这个对象上实现copy
和deepcopy
方法的正确方法是什么,以确保copy.copy
和copy.deepcopy
给我一个正确的行为?我可能有点不了解具体情况,但现在开始
从
- 浅复制构造一个新的复合对象,然后(尽可能)向其中插入对原始对象的引用
- 深度副本构造一个新的复合对象,然后递归地将在原始副本中找到的对象的副本插入其中
换句话说:copy()
将只复制顶部元素,而将其余元素作为指针保留到原始结构中deepcopy()
将递归复制所有内容
也就是说,deepcopy()
是您所需要的
如果您需要执行一些真正特定的操作,您可以按照手册中的说明覆盖\uuuuuu copy\uuuuuu()
或\uuuu deepcopy\uuuuuu()
。就我个人而言,我可能会实现一个简单的函数(例如,config.copy\u config()
或诸如此类),以明确它不是Python的标准行为。关于自定义的建议在文章的最后:
类可以使用相同的接口来
控制复制它们用于
控制酸洗。请参见说明
有关的信息,请参阅模块pickle
这些方法。“复制”模块不执行此操作
不使用复制注册
模块
为了让类定义自己的
复制实现,它可以定义
特殊方法\uuuuu copy\uuuuuu()
和
\uuuu deepcopy\uuuuuu()
。调用前者以实现浅层复制
活动不支持任何其他参数
通过。后者被称为
实施深度复制操作;信息技术
通过了一个论点,备忘录
字典。如果\uuuuu deepcopy\uuuuuu()
执行工作需要作出深入的努力
组件的副本,它应该调用
deepcopy()
组件作为第一个参数和
备忘录字典作为第二个参数
由于您似乎不关心酸洗定制,定义\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
和\uuuuuuuuuuuuuuuuuuuuuuuuuuuuu
具体来说,\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
def __copy__(self):
newone = type(self)()
newone.__dict__.update(self.__dict__)
return newone
\uuuu deepcopy\uuuuuu
类似(也接受备忘录
arg),但在返回之前,它必须调用self.foo=deepcopy(self.foo,memo)
,以获取任何需要深度复制的属性self.foo
(本质上是容器的属性——列表、dict、非基本对象,它们通过\uuu dict\uuu
保存其他内容)。将Alex Martelli的答案和Rob Young的评论放在一起,可以得到以下代码:
from copy import copy, deepcopy
class A(object):
def __init__(self):
print 'init'
self.v = 10
self.z = [2,3,4]
def __copy__(self):
cls = self.__class__
result = cls.__new__(cls)
result.__dict__.update(self.__dict__)
return result
def __deepcopy__(self, memo):
cls = self.__class__
result = cls.__new__(cls)
memo[id(self)] = result
for k, v in self.__dict__.items():
setattr(result, k, deepcopy(v, memo))
return result
a = A()
a.v = 11
b1, b2 = copy(a), deepcopy(a)
a.v = 12
a.z.append(5)
print b1.v, b1.z
print b2.v, b2.z
印刷品
init
11 [2, 3, 4, 5]
11 [2, 3, 4]
此处\uuuu deepcopy\uuuuu
填写备忘录
目录,以避免在对象本身从其成员引用的情况下过度复制。您的问题不清楚为什么需要覆盖这些方法,因为您不想对复制方法进行任何自定义
无论如何,如果您确实希望自定义深度副本(例如,通过共享某些属性并复制其他属性),以下是一个解决方案:
from copy import deepcopy
def deepcopy_with_sharing(obj, shared_attribute_names, memo=None):
'''
Deepcopy an object, except for a given list of attributes, which should
be shared between the original object and its copy.
obj is some object
shared_attribute_names: A list of strings identifying the attributes that
should be shared between the original and its copy.
memo is the dictionary passed into __deepcopy__. Ignore this argument if
not calling from within __deepcopy__.
'''
assert isinstance(shared_attribute_names, (list, tuple))
shared_attributes = {k: getattr(obj, k) for k in shared_attribute_names}
if hasattr(obj, '__deepcopy__'):
# Do hack to prevent infinite recursion in call to deepcopy
deepcopy_method = obj.__deepcopy__
obj.__deepcopy__ = None
for attr in shared_attribute_names:
del obj.__dict__[attr]
clone = deepcopy(obj)
for attr, val in shared_attributes.iteritems():
setattr(obj, attr, val)
setattr(clone, attr, val)
if hasattr(obj, '__deepcopy__'):
# Undo hack
obj.__deepcopy__ = deepcopy_method
del clone.__deepcopy__
return clone
class A(object):
def __init__(self):
self.copy_me = []
self.share_me = []
def __deepcopy__(self, memo):
return deepcopy_with_sharing(self, shared_attribute_names = ['share_me'], memo=memo)
a = A()
b = deepcopy(a)
assert a.copy_me is not b.copy_me
assert a.share_me is b.share_me
c = deepcopy(b)
assert c.copy_me is not b.copy_me
assert c.share_me is b.share_me
接下来,要实现定制的deepcopy,只需对默认实现进行最小的更改(例如,只需修改所需的字段):
基于Antony Hatchkins的清晰回答,下面是我的版本,其中所讨论的类来自另一个自定义类(s.t.我们需要称为super
):
复制
模块最终使用/酸洗协议,因此这些也是要覆盖的有效目标
默认实现只返回并设置类的\uuu dict\uu
,因此您不必调用super()
并担心埃诺·古尔丁的聪明把戏。它有效吗?有问题吗?我原以为我在共享参考资料方面仍有问题,但完全有可能我在其他地方搞砸了。我会根据@MortenSiebuhr的帖子再次检查,并更新结果。根据我目前有限的理解,我会d希望copy.deepcopy(chartconfiguinstance)返回一个新实例,该实例不会与原始实例有任何共享引用(无需自己重新实现deepcopy)。这是错误的吗?为了让类定义自己的副本实现,它可以定义特殊方法\u copy\u(
)和\u deepcopy\u()
。我会再次检查我的代码,谢谢。如果这是其他地方的一个简单错误,我会感到很愚蠢:-P@MortenSiebuhr你是对的。我不完全清楚copy/deepcopy是否会
class Foo(object):
def __deepcopy__(self, memo):
deepcopy_method = self.__deepcopy__
self.__deepcopy__ = None
cp = deepcopy(self, memo)
self.__deepcopy__ = deepcopy_method
cp.__deepcopy__ = deepcopy_method
# custom treatments
# for instance: cp.id = None
return cp
class Foo(FooBase):
def __init__(self, param1, param2):
self._base_params = [param1, param2]
super(Foo, result).__init__(*self._base_params)
def __copy__(self):
cls = self.__class__
result = cls.__new__(cls)
result.__dict__.update(self.__dict__)
super(Foo, result).__init__(*self._base_params)
return result
def __deepcopy__(self, memo):
cls = self.__class__
result = cls.__new__(cls)
memo[id(self)] = result
for k, v in self.__dict__.items():
setattr(result, k, copy.deepcopy(v, memo))
super(Foo, result).__init__(*self._base_params)
return result