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字典中重用现有的值,而不是创建一个新对象。
转载请注明来源: http://www.spiderpy.cn/blog/detail/38
Python标准库笔记(7) — copy模块的更多相关文章
- Python标准库笔记(11) — Operator模块
Operator--标准功能性操作符接口. 代码中使用迭代器时,有时必须要为一个简单表达式创建函数.有些情况这些函数可以用一个lambda函数实现,但是对于某些操作,根本没必要去写一个新的函数.因此o ...
- Python标准库笔记(10) — itertools模块
itertools 用于更高效地创建迭代器的函数工具. itertools 提供的功能受Clojure,Haskell,APL和SML等函数式编程语言的类似功能的启发.它们的目的是快速有效地使用内存, ...
- Python标准库笔记(9) — functools模块
functools 作用于函数的函数 functools 模块提供用于调整或扩展函数和其他可调用对象的工具,而无需完全重写它们. 装饰器 partial 类是 functools 模块提供的主要工具, ...
- Python标准库笔记(8) — pprint模块
struct模块提供了用于在字节字符串和Python原生数据类型之间转换函数,比如数字和字符串. Python版本: 2.x & 3.x 该模块作用是完成Python数值和C语言结构体的Pyt ...
- python标准库介绍——9 copy模块详解
==copy 模块== ``copy`` 模块包含两个函数, 用来拷贝对象, 如 [Example 1-64 #eg-1-64] 所示. ``copy(object) => object`` 创 ...
- Python标准库笔记(6) — struct模块
该模块作用是完成Python数值和C语言结构体的Python字符串形式间的转换.这可以用于处理存储在文件中或从网络连接中存储的二进制数据,以及其他数据源. 用途: 在Python基本数据类型和二进制数 ...
- Python 标准库笔记(1) — String模块
原文出处: j_hao104 String模块包含大量实用常量和类,以及一些过时的遗留功能,并还可用作字符串操作. 1. 常用方法 常用方法 描述 str.capitalize() 把字符串的首字母大 ...
- (转)Python 标准库笔记:string模块
String模块包含大量实用常量和类,以及一些过时的遗留功能,并还可用作字符串操作. 原文:http://www.10tiao.com/html/384/201709/2651305041/1.htm ...
- Python标准库笔记(1) — string模块
String模块包含大量实用常量和类,以及一些过时的遗留功能,并还可用作字符串操作. 1. 常用方法 常用方法 描述 str.capitalize() 把字符串的首字母大写 str.center(wi ...
随机推荐
- bzoj2095-Bridge
题意 一个 \(n\) 个点 \(m\) 条边的图,每条边双向都有权值(可能不一样).求从 1 开始,经过所有点,经过所有边一次且仅一次(即一定要经过这条边的某个方向)回到 1 的路径上权值最大的最小 ...
- Python内部类,内部类调用外部类属性,方法
一 Python中内部类 典型定义: class MyOuter: age=18 def __init__(self,name): self.name=name class MyInner: def ...
- java 操作命令行
目的:用java进行调用ab压测 window: cmd ab.exe linux: sh ab.sh 命令: abs -n 500 -c 10 https://www.baidu.com/ > ...
- Android自定义 Dialog 对话框
Android自定义Dialoghttp://www.cnblogs.com/and_he/archive/2011/09/16/2178716.html Android使用自定义AlertDialo ...
- 解题:CEOI 2017 Mousetrap
题外话: 这是制杖yd的交流题目 题面 首先把捕鼠夹所在的点提出来当根,然后这变成了一棵有根树,我们先来看耗子移动的影响 可以发现耗子往下走就回不来了,而且最后还会被困在一个叶子上,那么这个时候我们把 ...
- mysql 5.7 json 字段类型查找、修改
修改 json 里的数组字段 mysql> set @json = '{"test": [{"name": "laravel"}, { ...
- matlab绿色版本合集
网上找的matlab绿色版本合集链接https://pan.baidu.com/s/1gfDyIo7 密码:pxrs
- 实验三:将读取数据功能从Repository中分离
先理解下面这段话之后再开始做实验 经过实验二的改造之后,我们代码有一点结构的概念了: Listing.aspx:向Repository要Products,然后将得到的Products在页面中显示出来. ...
- python net-snmp使用
安装 官网:http://www.net-snmp.org/download.html 环境:CentOS 6.6 + python 2.7.10 1.下载安装包 net-snmp-5.6.2.1.t ...
- Mongodb 笔记03 查询、索引
查询 1. MongoDB使用find来进行查询.find的第一个参数决定了要返回哪些文档,这个参数是一个文档,用于指定查询条件.空的查询会匹配集合的全部内容.要是不指定查询,默认是{}. 2. 可以 ...