如何覆盖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