当前位置: 首页 > news >正文

Python Cookbook-6.9 快速复制对象

任务

为了使用 copy.copy,需要实现特殊方法__copy__。而且你的类的__init__比较耗时所以你希望能够绕过它并获得一个“空的”未初始化的类实例。

解决方案

下面的解决方案可同时适用于新风格和经典类:

def empty_copy(obj):class Empty(obj.__class__):def __init__(self):passnewcopy = Empty()newcopy.__class__ = obj.__class__return newcopy

你的类可以使用这个函数来实现__copy__:

class YourClass(object):def __init__(self):assume there's a lot of work heredef __copy__(self):newcopy = empty_copy(self)copy some relevant subset of self's attributes to newcopyreturn newcopy

下面是用法示例:

if __name__ == '__main__':import copyy = YourClass()#很显然,__init__会被调用print z = copy.copy(y)y#…不调用__init__print z

讨论

如同 4.1节的讨论,当你进行赋值操作时,Python 并不会暗中对对象进行复制操作,这是一个很好的处理,因为这样的机制更快、更灵活,而且也具有语义的一致性。当需要复制时,要明确地提出请求,通常使用 copy.copy 函数,它知道怎样复制内建的类型,对于自定义的对象也具有默认的行为方式,如果你想定制这个复制过程,可以定义类的__copy__特殊方法。如果你的类实例是不允许复制的,可以定义一个__copy__并抛出一个 TypeError 异常。大多数情况下,你只需要让 copy.copy 按照默认的机制工作就可以很轻松地获得克隆能力。这确实是很棒的设计,其他很多语言会强迫你实现个特殊的 clone 方法来完成实例克隆。

如果__init__是一个耗时的操作,copy__常常需要以一个“空”实例开始,从而绕开__init。最简单和通用的方法是使用Python 提供的直接修改实例的类的能力:在本地的空类中创建一个新对象,然后设置此对象的class属性,如代码所示。对旧风格(经典)类而言,从obj.class__继承 Empty 类显得比较多余(但也没什么害处)但继承机制使得本节的方案对于各种对象包括经典的和新风格的类(包括内建的和扩展类型),都具有很好的兼容性。一旦你选择从ob的类继承,必须重写 Empty 类中的__init,否则就无法实现本节方案的初衷。重写,意味着obj类的__init__不会被执行,这是因为在 Python 中,父类的初始化方法并不会自动执行。

一旦得到了一个符合要求的类的“空”对象,就需要复制 self 属性的一个子集。当需要所有属性时,最好就不要自定义__copy__,因为copy.copy 的默认行为就是复制实例的所有属性。除非你除了复制所有属性之外还想做更多的工作,在本例中,下面两种复制所有属性的方式都是可行的:

newcopy.__dict__.update(self.__dict__)
newcopy.__dict__ = dict(self.__dict__)

新风格类的实例并不一定会在__dict__中保存它的所有状态,所以你可能需要复制一些与类相关特定的状态。

基于标准模块 new 的方式对于经典类和新风格类无法做到完全透明,而且new静态方法也不能生成一个空的实例——后者只在新风格类中存在。不过,本节的方案能够解决这些问题。

实现__copy__的一种好的替代方式是实现__getstate__和__setstate__方法:这些特殊方法以明确的和原生的方式定义了你的对象的状态,绕过了__init__。另外,它们也支持类实例的序列化:见7.4节中关于这些方法的更多信息。

到此为止我们讨论的是浅拷贝,这也是你大多数时候需要的复制方式。使用浅拷贝时虽然你的对象被复制了,但是它指向的很多对象(内部的属性或者子项)却并未被复制,所以新复制的对象和原对象指向相同的属性和子项–这是一种轻量级的快速操作。而深拷贝则是一种重量级的深度操作,它根据对象的指向图谱逐一地完成对所有对象的拷贝。可以调用 copy.deepcopy 完成对一个对象的深拷贝。如果你想定制你的类实例的深拷贝方式,需要定义一个特殊方法__deepcopy__:

class YourClass(object):'''def __deepcopy__(self,memo):newcopy = empty_copy(self)#使用copy.deepcopy(self.x,memo)来获得self的相关属性#元素的子集的深拷贝,并放入newcopy中。return newcopy

如果决定要实现__deepcopy__,记住要遵守Python 标准库模块 copy 的文档定义的memoization 协议——应该复制有第二个参数的 copy.deepcopy要求的全部属性和子项同一个memo字典,还会被传递给__deepcopy__方法。另外,实现__getstate__和__setstate__也是很好的选择,因为这些方法同样支持深拷贝:Python 会处理并复制那些由__getstate__ 返回的“状态”对象,然后传递给一个新的,空实例的__setstate__方法。参看7.4 节关于这些特殊方法的更多内容。

相关文章:

  • 为什么vue的key值,不用index?
  • 文件传输过滤器绕过:Exe2Hex
  • 【资料推荐】LVDS Owner’s Manual
  • pcd2pgm的launch文件实现
  • [C]基础13.深入理解指针(5)
  • 第18章:MCP在创作领域中的应用
  • 表示学习与部分域适应
  • 第19章:Multi-Agent多智能体系统介绍
  • “IAmMusicFont.com“:将音乐变成视觉
  • 14.ArkUI Radio的介绍和使用
  • CSS Position 属性完全指南
  • 高精度运算
  • 【多目标进化算法】 MOEA/D算法(知识点)
  • PH热榜 | 2025-04-25
  • [Windows] 电脑清理加速:Windows Cleaner v5.0.5
  • ORB-SLAM3核心模块、数据结构和线程交互方面解析
  • const(C++)
  • 有关虚拟奢侈品
  • 如何创建和推广高质量内容:SEO与内容营销的成功指南
  • MySQL索引优化、SQL分析与运行原理 - Java架构师面试实战
  • 政治局会议深度|提出“设立新型政策性金融工具”有何深意?
  • 特朗普说克里米亚将留在俄罗斯,泽连斯基:绝不承认
  • 广汽集团一季度净亏损7.3亿元,同比转亏,总销量下滑9%
  • 成都一季度GDP为5930.3亿元,同比增长6%
  • 我国民营经济首季运行向新向好,对国民经济发展形成有力支撑
  • 韩国检方以受贿嫌疑起诉前总统文在寅