代码之家  ›  专栏  ›  技术社区  ›  Brent Writes Code

在python中,覆盖对象的copy/deepcopy操作的正确方法是什么?

  •  73
  • Brent Writes Code  · 技术社区  · 15 年前

    所以,为了确定,我觉得我理解 copy VS deepcopy 在复制模块中,我使用了 copy.copy copy.deepcopy 在成功之前,但这是我第一次真正开始超载 __copy__ __deepcopy__ 方法。我已经到处搜索并浏览了内置的python模块以查找 圣保罗 第六章 功能(例如) sets.py , decimal.py fractions.py 但我仍然不能百分之百地确定我做得对。

    以下是我的场景:

    我有一个配置对象,它主要由简单的属性组成(尽管它可能包含其他非基本对象的列表)。最初,我将用一组默认值实例化一个配置对象。此配置将移交给多个其他对象(以确保所有对象都以相同的配置开始)。然而,一旦用户交互开始,每个对象将需要能够独立地调整配置,而不会影响彼此的配置(这对我来说,我需要制作初始配置的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 
    

    正确的方法是什么 复制 Deepcopy公司 此对象的方法以确保 复印件 复印件 给我适当的行为?我目前使用的是python 2.6.2。

    事先谢谢!

    7 回复  |  直到 6 年前
        1
  •  62
  •   Alex Martelli    15 年前

    自定义的建议位于 docs page :

    类可以使用相同的接口 控制复制他们使用的 控制酸洗。参见说明 关于模块pickle的信息 这些方法。复制模块可以 不使用复制注册 模块。

    以便类定义自己的 复制实现,它可以定义 特殊方法 __copy__() __deepcopy__() . 前者被调用来实现浅拷贝 操作;没有其他参数 通过。后者被称为 执行深度复制操作;IT 通过了一个论点,备忘录 字典。如果 Y-深拷贝() 实施需要做一个深刻的 组件的副本,它应该调用 这个 deepcopy() 函数 组件作为第一个参数 备忘录字典作为第二个参数。

    因为你似乎不关心酸洗定制,定义 __copy__ __deepcopy__ 这似乎是你的正确选择。

    明确地, 圣保罗 (浅显的副本)在你的情况下很容易……:

    def __copy__(self):
      newone = type(self)()
      newone.__dict__.update(self.__dict__)
      return newone
    

    第六章 类似(接受 memo arg)但在返回之前,它必须调用 self.foo = deepcopy(self.foo, memo) 对于任何属性 self.foo 需要进行深度复制(本质上属性是容器——列表、字典、非原始对象,这些对象通过它们保存其他内容 __dict__ s)。

        2
  •  67
  •   Antony Hatchkins Alexander Hamilton    11 年前

    将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以避免在对象本身从其成员引用时过度复制。

        3
  •  7
  •   Community Egal    7 年前

    跟随 Peter's excellent answer ,要实现自定义的deepcopy,只需对默认实现进行最小的更改(例如,只需根据需要修改字段):

    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
    
        4
  •  5
  •   Morten Siebuhr    8 年前

    我可能对细节有点不太了解,但下面是:

    copy docs ;

    • 一个浅拷贝构造一个新的复合对象,然后(在可能的范围内)向其中插入对原始对象的引用。
    • 深度复制构造新的复合对象,然后递归地将原始对象的副本插入其中。

    换言之: copy() 将只复制顶级元素,其余元素保留为指向原始结构的指针。 deepcopy() 将递归地复制所有内容。

    也就是说, 深拷贝() 是你需要的。

    如果您需要做一些非常具体的事情,可以覆盖 __copy__() __deepcopy__() ,如手册所述。就个人而言,我可能会实现一个简单的函数(例如 config.copy_config() 或者类似的)让人明白这不是Python标准的行为。

        5
  •  5
  •   Peter    7 年前

    您的问题并不清楚为什么需要重写这些方法,因为您不想对复制方法进行任何自定义。

    无论如何,如果您想自定义深度复制(例如通过共享一些属性和复制其他属性),这里有一个解决方案:

    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
    
        6
  •  1
  •   BoltzmannBrain    6 年前

    基于安东尼·哈金斯的清晰答案,这里是我的版本,在这个版本中,所讨论的类来自另一个自定义类(S.T.我们需要调用 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
    
        7
  •  0
  •   ankostis    6 年前

    这个 copy 模块使用了 __getstate__() / __setstate__() 酸洗协议 ,因此这些也是要重写的有效目标。

    默认实现只返回并设置 __dict__ 所以你不必打电话 super() 担心艾诺·古丁的聪明把戏, above .