Python标准库笔记(7) — copy模块
目录[-]
copy-对象拷贝模块;提供了浅拷贝和深拷贝复制对象的功能, 分别对应模块中的两个函数
copy()
和deepcopy()
。
1.浅拷贝(Shallow Copies)
copy()
创建的 浅拷贝 是一个新的容器,它包含了对原始对象的内容的引用。也就是说仅拷贝父对象,不会拷贝对象的内部的子对象。即浅复制只复制对象本身,没有复制该对象所引用的对象。比如,当创建一个列表对象的浅拷贝时,将构造一个新的列表,并将原始对象的元素添加给它。
import copy
class MyClass:
def __init__(self, name):
self.name = name
def __eq__(self, other):
return self.name == other.name
def __gt__(self, other):
return self.name > other.name
a = MyClass('a')
my_list = [a]
dup = copy.copy(my_list)
print(' my_list:', my_list)
print(' dup:', dup)
print(' dup is my_list:', (dup is my_list))
print(' dup == my_list:', (dup == my_list))
print('dup[0] is my_list[0]:', (dup[0] is my_list[0]))
print('dup[0] == my_list[0]:', (dup[0] == my_list[0]))
my_list: [<__main__.MyClass object at 0x0000026DFF98D128>]
dup: [<__main__.MyClass object at 0x0000026DFF98D128>]
dup is my_list: False
dup == my_list: True
dup[0] is my_list[0]: True
dup[0] == my_list[0]: True
上面的浅拷贝实例中,dup
是由 my_list
拷贝而来, 但是 MyClass
实例不会拷贝,所以 dup
列表与 my_list
中引用的是同一个对象。
2.深拷贝(Deep Copies)
deepcopy()
创建的 深拷贝 是一个新的容器,它包含了对原始对象的内容的拷贝。深拷贝完全拷贝了父对象及其子对象。即创建一个新的组合对象,同时递归地拷贝所有子对象,新的组合对象与原对象没有任何关联。虽然实际上会共享不可变的子对象,但不影响它们的相互独立性。
将上面代码换成 deepcopy()
,将会发现其中不同:
import copy
class MyClass:
def __init__(self, name):
self.name = name
def __eq__(self, other):
return self.name == other.name
def __gt__(self, other):
return self.name > other.name
a = MyClass('a')
my_list = [a]
dup = copy.deepcopy(my_list)
print(' my_list:', my_list)
print(' dup:', dup)
print(' dup is my_list:', (dup is my_list))
print(' dup == my_list:', (dup == my_list))
print('dup[0] is my_list[0]:', (dup[0] is my_list[0]))
print('dup[0] == my_list[0]:', (dup[0] == my_list[0]))
my_list: [<__main__.MyClass object at 0x000002442E47D128>]
dup: [<__main__.MyClass object at 0x00000244352EF208>]
dup is my_list: False
dup == my_list: True
dup[0] is my_list[0]: False
dup[0] == my_list[0]: True
列表中的 MyClass
实例不再是同一个的对象引用,而是重新复制了一份, 但是当两个对象被比较时,它们的值仍然是相等的。
3.自定义拷贝行为
可以通过自定义 __copy__()
和 __deepcopy__()
方法来改变默认的拷贝行为。
-
__copy()__
是一个无参数方法,它返回一个浅拷贝对象; -
__deepcopy()__
接受一个备忘(memo)字典参数,返回一个深拷贝对象。需要进行深拷贝的成员属性都应该传递给copy.deepcopy()
,以及memo字典,以控制递归。(下面例子将解释memo字典)。
下面的示例演示如何调用这些方法:
import copy
class MyClass:
def __init__(self, name):
self.name = name
def __eq__(self, other):
return self.name == other.name
def __gt__(self, other):
return self.name > other.name
def __copy__(self):
print('__copy__()')
return MyClass(self.name)
def __deepcopy__(self, memo):
print('__deepcopy__({})'.format(memo))
return MyClass(copy.deepcopy(self.name, memo))
a = MyClass('a')
sc = copy.copy(a)
dc = copy.deepcopy(a)
__copy__()
__deepcopy__({})
memo字典用于跟踪已经拷贝的值,以避免无限递归。
4.深拷贝中的递归
为了避免拷贝时有递归数据结构的问题, deepcopy()`` 使用一个字典来跟踪已经拷贝的对象。这个字典被传递给
deepcopy()` 方法进行检查。
下面示例展示了一个相互关联的数据结构(有向图),如何通过实现 __deepcopy__()
方法来防止递归。
import copy
class Graph:
def __init__(self, name, connections):
self.name = name
self.connections = connections
def add_connection(self, other):
self.connections.append(other)
def __repr__(self):
return 'Graph(name={}, id={})'.format(
self.name, id(self))
def __deepcopy__(self, memo):
print('nCalling __deepcopy__ for {!r}'.format(self))
if self in memo:
existing = memo.get(self)
print(' Already copied to {!r}'.format(existing))
return existing
print(' Memo dictionary:')
if memo:
for k, v in memo.items():
print(' {}: {}'.format(k, v))
else:
print(' (empty)')
dup = Graph(copy.deepcopy(self.name, memo), [])
print(' Copying to new object {}'.format(dup))
memo[self] = dup
for c in self.connections:
dup.add_connection(copy.deepcopy(c, memo))
return dup
root = Graph('root', [])
a = Graph('a', [root])
b = Graph('b', [a, root])
root.add_connection(a)
root.add_connection(b)
dup = copy.deepcopy(root)
Graph
类包括一些基本的有向图方法。可以用一个名称和它所连接的现有节点的列表来初始化一个实例。 add_connection()
方法用于设置双向连接。它也被深拷贝操作符使用。
__deepcopy__()
方法打印了它的调用信息,并根据需要管理memo字典内容。它不会复制整个连接列表,而是创建一个新的列表,并将单个连接的副本添加进去。确保在每个新节点被复制时更新memo字典,并且避免递归或重复拷贝节点。与以前一样,该方法在完成时返回拷贝的对象。
Calling __deepcopy__ for Graph(name=root, id=2115579269360)
Memo dictionary:
(empty)
Copying to new object Graph(name=root, id=2115695211072)
Calling __deepcopy__ for Graph(name=a, id=2115695210904)
Memo dictionary:
Graph(name=root, id=2115579269360): Graph(name=root, id=2115695211072)
Copying to new object Graph(name=a, id=2115695211184)
Calling __deepcopy__ for Graph(name=root, id=2115579269360)
Already copied to Graph(name=root, id=2115695211072)
Calling __deepcopy__ for Graph(name=b, id=2115695210960)
Memo dictionary:
Graph(name=root, id=2115579269360): Graph(name=root, id=2115695211072)
Graph(name=a, id=2115695210904): Graph(name=a, id=2115695211184)
2115579269360: Graph(name=root, id=2115695211072)
2115695219408: [Graph(name=root, id=2115579269360), Graph(name=a, id=2115695210904)]
2115695210904: Graph(name=a, id=2115695211184)
Copying to new object Graph(name=b, id=2115695211240)
第二次遇到根节点时,如果一个节点被已拷贝时, __deepcopy__()
检测递归,并从memo字典中重用现有的值,而不是创建一个新对象。
- Numpy教程第1部分 - 阵列简介(常用基础操作总结)
- Session和Cookies的基本原理
- 浅析Numpy.genfromtxt及File I/O讲解
- 损失函数详解
- 排查Java的内存问题
- 使用两种方法让 ASP.NET Core 实现遵循 HATEOAS 结构的 RESTful API
- 设计模式六大原则(5):迪米特法则
- Selenium2+python自动化61-Chrome浏览器(chromedriver)
- 区块链可以减少社会不平等吗?
- 【干货】不止准确率:为分类任务选择正确的机器学习度量指标(附代码实现)
- python爬虫beautifulsoup4系列1
- 区块链入门教程
- python爬虫beautifulsoup4系列2
- python爬虫beautifulsoup4系列3
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 详解Android Gradle插件3.0挖坑日记
- Android开发之拼音转换工具类PinyinUtils示例
- Android多线程断点续传下载示例详解
- Android设备与外接U盘实现数据读取操作的示例
- [Alibaba-ARouter]浅谈简单好用的Android页面路由框架
- android屏幕圆角实现方法的示例代码
- Android开发中日期工具类DateUtil完整实例
- Android模仿实现微博详情页滑动固定顶部栏的效果实例
- Android EventBus(普通事件/粘性事件)详解
- Android实现EventBus登录界面与传值(粘性事件)
- Android自定义LinearLayout布局显示不完整的解决方法
- android短信管理器SmsManager实例详解
- Android开发判断一个app应用是否在运行的方法详解
- 收割腾讯等十几个Offer后,揭秘进大厂的秘诀和Android技术面试题汇总!
- Flutter BLoC 异步通信、BlocBuilder的基本使用、BlocProvider的初探