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 ...
随机推荐
- springmvc+json 前后台数据交互
1. 配置(1) 文件配置参考这里(2) 导入jackson相关包:jackson-annotations-2.9.4.jar,jackson-core-2.9.4.jar,jackson-datab ...
- Qss 样式表的尝试
QLineEdit{ border:1px solid #137eb6; padding:2px; background-color:#F5F5F5; } QToolTip{ border:1px s ...
- BZOJ3158 千钧一发(最小割)
可以看做一些物品中某些互相排斥求最大价值.如果这是个二分图的话,就很容易用最小割了. 观察其给出的条件间是否有什么联系.如果两个数都是偶数,显然满足条件二:而若都是奇数,则满足条件一,因为式子列出来发 ...
- 【刷题】HDU 3435 A new Graph Game
Problem Description An undirected graph is a graph in which the nodes are connected by undirected ar ...
- CF17E Palisection——优秀的综合计数题
题意翻译 给定一个长度为n的小写字母串.问你有多少对相交的回文子 串(包含也算相交) . 输入格式 第一行是字符串长度n(1<=n<=2*10^6),第二行字符串 输出格式 相交的回文子串 ...
- 【arc073D】Many Moves
Portal -->arc073D Description 有\(n\)个格子,编号从左到右为\(1\sim n\),一开始有两个棋子,位置给定,接下来要依次进行\(Q\)次操作,第\(i\ ...
- apt代理设置
内网apt使用代理 /etc/apt/apt.conf Acquire::http::Proxy "http://guest:password@ip:port";
- Java 从业一年的心得体会
在你打开此文时,你或许在犹豫这个职业,但是我觉得干就好了,没有适合不适合,趁年轻折腾吧! 以下是我一年来从事Java的经验积累,知识有很多,经验就九条 1.设计的数据库表尽量添加一个状态位,可以在删除 ...
- IOS子视图超过父视图frame后,无法交互响应
确定第一响应者 当用户触发某一事件(触摸事件或运动事件)后,UIKit会创建一个事件对象(UIEvent),该对象包含一些处理事件所需要的信息.然后事件对象被放到一个事件队列中.这些事件按照先进先出的 ...
- windows10安装ipython jupyter
环境: windows 10 x64 python2.7(已经安装好numpy scipy matplotlib) 过程: 首先安装visual c++ 9.0环境,去http://aka.ms/vc ...