什么是正确的方式来覆盖Python中的对象的复制/深层拷贝操作?
所以,为了build立起来,我觉得我理解copy模块和copy.copy
模块之间的差异,并且在成功之前使用了copy.copy
和copy.deepcopy
,但是这是我第一次实际上重载__copy__
和__deepcopy__
方法。 我已经search了__copy__
,查看了内置的Python模块来查找__copy__
和__deepcopy__
函数(例如sets.py
, decimal.py
和fractions.py
)的实例,但是我仍然不能100%确定我说得对。
这是我的场景:
我有一个configuration对象,主要是由简单的属性组成(尽pipe它可能会有其他非原始对象的列表)。 最初,我将使用默认的一组值来实例化一个configuration对象。 该configuration将被交给多个其他对象(以确保所有对象以相同的configuration启动)。 但是,一旦用户交互开始,每个对象都需要能够独立地调整configuration,而不会影响对方的configuration(这对我说,我需要对我的初始configuration进行更深入的处理)。
这是一个示例对象:
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
给我正确的行为? 我目前正在使用Python 2.6.2。
提前致谢!
定制的build议在文档页面的最后 :
类可以使用相同的接口来控制他们用来控制酸洗的复制。 有关这些方法的信息,请参阅模块pickle的说明。 复制模块不使用copy_reg注册模块。
为了让类定义自己的拷贝实现,它可以定义特殊的方法
__copy__()
和__deepcopy__()
。 前者被称为执行浅拷贝操作; 没有额外的parameter passing。 后者被称为执行深层复制操作; 它传递了一个参数,即备忘录字典。 如果__deepcopy__()
实现需要创build组件的深层副本,则应该使用组件的第一个参数和备注字典作为第二个参数来调用deepcopy()
函数。
既然你看起来不在乎酸洗的定制,定义__copy__
和__deepcopy__
绝对是你正确的select。
具体来说, __copy__
copy __copy__
(浅拷贝)在你的情况下是非常容易的…:
def __copy__(self): newone = type(self)() newone.__dict__.update(self.__dict__) return newone
__deepcopy__
也是类似的(也接受memo
参数),但在返回之前,必须调用self.foo = deepcopy(self.foo, memo)
才能self.foo
需要深度复制的属性self.foo
(本质上属性是容器 -列表,字典,通过它们的__dict__
来保存其他东西的非原始对象)。
把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() av = 11 b1, b2 = copy(a), deepcopy(a) av = 12 azappend(5) print b1.v, b1.z print b2.v, b2.z
版画
init 11 [2, 3, 4, 5] 11 [2, 3, 4]
这里__deepcopy__
填写memo
字典,以避免过度复制,以防对象本身从其成员引用。
我可能会在具体细节上有点偏离,但是在这里。
从copy
文档 ;
- 浅拷贝构造一个新的复合对象,然后(尽可能)将引用插入到原始对象中。
- 深层副本构造一个新的复合对象,然后recursion地将副本插入到原始对象中。
换句话说: copy()
将只复制顶层元素,并将剩下的指针放在原始结构中。 deepcopy()
将以recursion方式复制一切。
也就是说, deepcopy()
就是你所需要的。
如果您需要完成特定的操作,可以按照手册中所述覆盖__copy__()
或__deepcopy__()
。 就个人而言,我可能会实现一个普通的函数(例如config.copy_config()
等)来说明它不是Python的标准行为。
遵循Peter的优秀回答 ,实现一个自定义的深层拷贝,只需要对默认实现进行最小的修改(例如只需要修改一个字段就可以):
class Foo(object): def __deepcopy__(self, memo): deepcopy_method = self.__deepcopy__ self.__deepcopy__ = None cp = deepcopy(self, memo) self.__deepcopy__ = deepcopy_method # custom treatments # for instance: cp.id = None return cp
从您的问题中不清楚为什么您需要重写这些方法,因为您不想对复制方法进行任何自定义。
无论如何,如果你想定制深层复制(例如通过共享一些属性和复制他人),这里是一个解决scheme:
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