Python标准库-深拷贝和浅拷贝,及其自定义

  |  

摘要: Python 标准库 copy 模块,深拷贝与浅拷贝

【对算法,数学,计算机感兴趣的同学,欢迎关注我哈,阅读更多原创文章】
我的网站:潮汐朝夕的生活实验室
我的公众号:算法题刷刷
我的知乎:潮汐朝夕
我的github:FennelDumplings
我的leetcode:FennelDumplings


1. Python 深拷贝与浅拷贝

Python 的赋值语句不复制对象,而是创建目标和对象的绑定关系。对于自身可变,或包含可变项的集合,有时要生成副本用于改变操作,而不必改变原始对象。本模块提供了通用的浅层复制和深层复制操作。

  • 浅拷贝:copy.copy
  • 深拷贝:copy.deepcopy

参考书(中英文版):

浅层与深层复制的区别

深拷贝与浅拷贝的区别仅与复合对象(容器对象或包含类实例的对象)相关:

  • 浅层复制: 构造一个新的复合对象,然后在尽可能的范围内将原始对象中找到的对象的引用插入其中。
  • 深层复制: 构造一个新的复合对象,然后递归地将在原始对象里找到的对象的副本插入其中。

深拷贝的问题

  1. 递归对象 (直接或间接包含对自身引用的复合对象) 可能会导致递归循环。
  2. 由于深层复制会复制所有内容,因此可能会过多复制(例如本应该在副本之间共享的数据)。

deepcopy 如何避免深拷贝的问题

  • 保留在当前复制过程中已复制的对象的 “备忘录” (memo) 字典
  • 允许用户定义的类重载复制操作或复制的组件集合

字典和列表的浅拷贝

字典的浅拷贝

1
dict2 = dict1.copy()

列表的浅拷贝可以用切片

1
l2 = l1[:]

自定义拷贝操作

__copy__(): 不传入额外参数
__deepcopy__(): 应传入 memo 字典,所有要深拷贝的成员属性都需要跟 memo 一起传入 copy.deepcopy()。


2. 例子: 深浅拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import copy
import functools

@functools.total_ordering
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("czx")
my_list = [a]
shallow_dup = copy.copy(my_list)
deep_dup = copy.deepcopy(my_list)

print("my_list: {}".format(my_list))
print("shallow_dup: {}".format(shallow_dup))
print("deep_dup: {}".format(deep_dup))
print("shallow_dup is my_list: {}".format(shallow_dup is my_list))
print("shallow_dup == my_list: {}".format(shallow_dup == my_list))
print("deep_dup is my_list: {}".format(deep_dup is my_list))
print("deep_dup == my_list: {}".format(deep_dup == my_list))
print("shallow_dup[0] is my_list[0]: {}".format(shallow_dup[0] is my_list[0]))
print("shallow_dup[0] == my_list[0]: {}".format(shallow_dup[0] == my_list[0]))
print("deep_dup[0] is my_list[0]: {}".format(deep_dup[0] is my_list[0]))
print("deep_dup[0] == my_list[0]: {}".format(deep_dup[0] == my_list[0]))
1
2
3
4
5
6
7
8
9
10
11
my_list: [<__main__.MyClass object at 0x7f9d6383d550>]
shallow_dup: [<__main__.MyClass object at 0x7f9d6383d550>]
deep_dup: [<__main__.MyClass object at 0x7f9d62297d68>]
shallow_dup is my_list: False
shallow_dup == my_list: True
deep_dup is my_list: False
deep_dup == my_list: True
shallow_dup[0] is my_list[0]: True
shallow_dup[0] == my_list[0]: True
deep_dup[0] is my_list[0]: False
deep_dup[0] == my_list[0]: True

3. 例子: 自定义深浅拷贝操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import copy
import functools

@functools.total_ordering
class MyClass:
def __init__(self, name1, name2):
self.name1 = name1
self.name2 = name2

def __eq__(self, other):
return self.name1 == other.name1 and self.name2 == other.name2

def __gt__(self, other):
if self.name1 == other.name1:
return self.name2 > other.name2
return self.name1 > other.name1

def __copy__(self):
print("__copy__()")
return MyClass(self.name1, self.name2)

def __deepcopy__(self, memo):
print("__deepcopy__({})".format(memo))
return MyClass(copy.deepcopy(self.name1, memo)
,copy.deepcopy(self.name2, memo)
)

a = MyClass("czx", "momo")
sc = copy.copy(a)
dc = copy.deepcopy(a)
1
2
__copy__()
__deepcopy__({})

4. 例子: 深拷贝一个带环的有向图

用备忘录处理环的问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Calling __deepcopy__ for Graph(name=root, id=140348924760920)
Memo dictionary:
(empty)
Copying to new object Graph(name=root, id=140348902074184)

Calling __deepcopy__ for Graph(name=a, id=140348901990072)
Memo dictionary:
Graph(name=root, id=140348924760920): Graph(name=root, id=140348902074184)
Copying to new object Graph(name=a, id=140348902075640)

Calling __deepcopy__ for Graph(name=root, id=140348924760920)
Already copied to Graph(name=root, id=140348902074184)

Calling __deepcopy__ for Graph(name=b, id=140348901990184)
Memo dictionary:
Graph(name=root, id=140348924760920): Graph(name=root, id=140348902074184)
Graph(name=a, id=140348901990072): Graph(name=a, id=140348902075640)
140348924760920: Graph(name=root, id=140348902074184)
140348902001112: [Graph(name=root, id=140348924760920), Graph(name=a, id=140348901990072)]
140348901990072: Graph(name=a, id=140348902075640)
Copying to new object Graph(name=b, id=140348902075696)

Share