我了解复制模块中copy
与deepcopy
之间的区别。我在成功之前使用了copy.copy
和copy.deepcopy
,但这是我第一次真正重载__copy__
和__deepcopy__
方法。我已经用Google搜索并查看了内置的Python模块,以查找__copy__
和__deepcopy__
函数的实例(例如sets.py
,decimal.py
和{{ 1}}),但我仍然不能100%确定我做对了。
这是我的情景:
我有一个配置对象。最初,我将使用一组默认值来实例化一个配置对象。此配置将切换到多个其他对象(以确保所有对象以相同的配置启动)。但是,一旦用户交互开始,每个对象都需要独立调整其配置,而不会影响彼此的配置(对我来说,我需要使用我的初始配置的深度复制来处理)。
这是一个示例对象:
fractions.py
在此对象上实施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
给我正确的行为?
答案 0 :(得分:74)
将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]
此处__deepcopy__
填写memo
dict以避免在对象本身从其成员引用的情况下进行过多复制。
答案 1 :(得分:62)
自定义建议位于docs page:
的最后类可以使用相同的接口 控制他们使用的复制 控制酸洗。请参阅说明 模块pickle的信息 这些方法。复制模块 不要使用copy_reg注册 模块。
为了让一个类定义自己的 复制实现,它可以定义 特殊方法
__copy__()
和__deepcopy__()
。前者被称为实现浅拷贝 操作;没有其他参数 通过。后者被称为 实施深拷贝操作;它 通过一个参数,备忘录 字典。如果__deepcopy__()
实施需要深入 一个组件的副本,它应该调用deepcopy()
函数与 组件作为第一个参数和 备忘录词典作为第二个论点。
由于您似乎不关心腌制自定义,因此定义__copy__
和__deepcopy__
似乎是正确的方法。
具体来说,__copy__
(浅层副本)在您的情况下非常简单......:
def __copy__(self):
newone = type(self)()
newone.__dict__.update(self.__dict__)
return newone
__deepcopy__
也类似(也接受memo
arg)但在返回之前,必须为需要深度复制的任何属性self.foo = deepcopy(self.foo, memo)
调用self.foo
(实质上属性是容器 - 列表,dicts,非原始对象,通过__dict__
来保存其他东西。
答案 2 :(得分:10)
关注Peter's excellent answer,实现自定义深度复制,对默认实现进行最少的更改(例如只修改我需要的字段):
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
答案 3 :(得分:5)
我可能会对细节有所了解,但是这里可以了;
来自copy
docs;
- 浅复制构造一个新的复合对象,然后(尽可能)将对它的引用插入到原始对象中找到的对象。
- 深层复制构造一个新的复合对象,然后以递归方式将副本插入到原始对象中找到的对象。
换句话说:copy()
将仅复制顶部元素,并将其余元素作为指向原始结构的指针。 deepcopy()
将以递归方式复制所有内容。
也就是说,deepcopy()
就是您所需要的。
如果您需要做一些非常具体的事情,可以覆盖__copy__()
或__deepcopy__()
,如手册中所述。就个人而言,我可能会实现一个普通函数(例如config.copy_config()
或类似函数),以明确它不是Python标准行为。
答案 4 :(得分:5)
从您的问题中不清楚为什么需要覆盖这些方法,因为您不想对复制方法进行任何自定义。
无论如何,如果您确实想要自定义深层副本(例如,通过共享某些属性并复制其他属性),这是一个解决方案:
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
答案 5 :(得分:1)
copy
模块逐渐使用__getstate__()
/ __setstate__()
腌制协议,因此这些也是有效的覆盖目标。
默认实现只返回并设置班级的__dict__
,因此您不必致电super()
并担心Eino Gourdin的聪明伎俩,{{3 }}
答案 6 :(得分:1)
Peter 和 Eino Gourdin 的回答既聪明又有用,但它们有一个非常微妙的错误!
Python 方法绑定到它们的对象。当您执行 cp.__deepcopy__ = deepcopy_method
时,您实际上是在给对象 cp
引用 __deepcopy__
在原始对象上。对 cp.__deepcopy__
的任何调用都将返回原始副本!
如果您深度复制对象,然后深度复制该副本,则输出是不是副本的副本!
这是行为的一个最小示例,以及我的固定实现,您复制 __deepcopy__
实现然后将其绑定到新对象:
from copy import deepcopy
import types
class Good:
def __init__(self):
self.i = 0
def __deepcopy__(self, memo):
deepcopy_method = self.__deepcopy__
self.__deepcopy__ = None
cp = deepcopy(self, memo)
self.__deepcopy__ = deepcopy_method
# Copy the function object
func = types.FunctionType(
deepcopy_method.__code__,
deepcopy_method.__globals__,
deepcopy_method.__name__,
deepcopy_method.__defaults__,
deepcopy_method.__closure__,
)
# Bind to cp and set
bound_method = func.__get__(cp, cp.__class__)
cp.__deepcopy__ = bound_method
return cp
class Bad:
def __init__(self):
self.i = 0
def __deepcopy__(self, memo):
deepcopy_method = self.__deepcopy__
self.__deepcopy__ = None
cp = deepcopy(self, memo)
self.__deepcopy__ = deepcopy_method
cp.__deepcopy__ = deepcopy_method
return cp
x = Bad()
copy = deepcopy(x)
copy.i = 1
copy_of_copy = deepcopy(copy)
print(copy_of_copy.i) # 0
x = Good()
copy = deepcopy(x)
copy.i = 1
copy_of_copy = deepcopy(copy)
print(copy_of_copy.i) # 1
答案 7 :(得分:0)
在Antony Hatchkins的干净答案的基础上,这是我的版本,其中有问题的类派生自另一个自定义类(我们需要调用super
):
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