Python中的几种拷贝方式

提到拷贝,主要使用赋值、copy模块、列表切片、字典copy() 来实现拷贝。

赋值

赋值产生的拷贝是浅拷贝,共享地址。

改变任意一个对象中元素的值,会同时影响拷贝对象,当前对象怎么变,拷贝的对象就跟着怎么变。

1
2
3
4
5
6
7
8
9
10
11
list1 = [1,2,3,4,5]
list2 = list1
list2[0] = 100
print(list1,id(list1)) # [100, 2, 3, 4, 5] 2166018012616
print(list2,id(list2)) # [100, 2, 3, 4, 5] 2166018012616

list1 = [[1,2,3],2,3,4,5]
list2 = list1
list2[0][0] = 100
print(list1,id(list1)) # [[100, 2, 3], 2, 3, 4, 5] 1932839048904
print(list2,id(list2)) # [[100, 2, 3], 2, 3, 4, 5] 1932839048904

Copy

浅拷贝 copy.copy()

对于不可变类型:数字、字符串、元组, 浅拷贝仅仅是地址,即就是使用相同的地址,引用相同的地址id,不会开辟新空间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import copy

iNum = 1
iNewNum = copy.copy(iNum)
print(iNum,id(iNum)) # 1 1969923168
print(iNewNum,id(iNewNum)) # 1 1969923168

# 对于可变类型:列表、字典、集合,浅拷贝会开辟新的空间地址
# (仅仅是最外层开辟了新的空间,里层的元素地址还是相同的)
lstchild = ['a','b']
lst = [lstchild, 1, 2, 3]
lstCopy = copy.copy(lst)
print(lst,id(lst)) # [['a', 'b'], 1, 2, 3] 2419428511048
print(lstCopy,id(lstCopy)) # [['a', 'b'], 1, 2, 3] 2419428455688
print(id(lst[0])) # 1539324376328
print(id(lstCopy[0])) # 1539324376328
# 可以看出浅拷贝前后两个列表的地址是不同的,但是子元素列表的地址是相同的。
1
2
3
4
5
6
7
8
9
10
11
12
# 由上面可知,浅拷贝后,改变任意一个对象中不可变类型的元素的值,只有当前对象受影响,不影响拷贝的对象;
# 改变任意一个对象中为可变类型的元素的值,会同时影响拷贝对象的。
lst[0][0] = 'aa'
lst[1] = 11
print(lst) # [['aa', 'b'], 11, 2, 3]
print(lstCopy) # [['aa', 'b'], 1, 2, 3]
# 改变lstCopy同理

# 注意:若改变任意一个对象中一个可变类型(整个,不是其中的某个元素),也只会影响当前对象。
lst[0] = "ab"
print(lst) # ['ab', 11, 2, 3]
print(lstCopy) # [['aa', 'b'], 1, 2, 3]

深拷贝 copy.deepcopy()

copy.deepcopy() 除了外层拷贝,还对子元素也进行了拷贝(本质上递归浅拷贝)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 原对象和拷贝对象地址不同,所有的元素地址也不同
import copy
lstchild = ['a','b']
lst = [lstchild, 1, 2, 3]
lstCopy = copy.deepcopy(lst)
print(lst,id(lst)) # [['a', 'b'], 1, 2, 3] 2096824546824
print(lstCopy,id(lstCopy)) # [['a', 'b'], 1, 2, 3] 2096824601928
print(id(lst[0])) # 2096824547592
print(id(lstCopy[0])) # 2096824546696

# 深拷贝后,改变任意一个对象中元素的值,只有当前对象受影响
lst[0][0] = 'aa'
lst[1] = 11
print(lst) # [['aa', 'b'], 11, 2, 3]
print(lstCopy) # [['a', 'b'], 1, 2, 3]

列表切片

常使用的列表切片,也是浅拷贝,效果和 copy.copy() 相同

1
2
3
4
5
6
7
8
9
10
11
12
lstchild = ['a','b']
lst = [lstchild, 1, 2, 3]
lstCopy = lst[:]
print(lst,id(lst)) # [['a', 'b'], 1, 2, 3] 2096824546824
print(lstCopy,id(lstCopy)) # [['a', 'b'], 1, 2, 3] 2096824601928
print(id(lst[0])) # 2685187220808
print(id(lstCopy[0])) # 2685187220808

lst[0][0] = 'aa'
lst[1] = 11
print(lst) # [['aa', 'b'], 11, 2, 3]
print(lstCopy) # [['aa', 'b'], 1, 2, 3]

字典拷贝

字典自带有 copy() 函数,这种拷贝为浅拷贝

1
2
3
4
5
6
7
8
9
10
11
12
original_dict = {'a': 1, 'b': [2, 3], 'c': {'d': 4}}
copied_dict = original_dict.copy()

# 修改原字典中的不可变对象(如整数)不会影响拷贝
original_dict['a'] = 10 # 这不会影响 copied_dict

# 修改原字典中的可变对象(如列表或字典)会同时影响拷贝
original_dict['b'].append(4) # 这会影响 copied_dict
original_dict['c']['d'] = 5 # 这也会影响 copied_dict

print(original_dict) # {'a': 10, 'b': [2, 3, 4], 'c': {'d': 5}}
print(copied_dict) # {'a': 1, 'b': [2, 3, 4], 'c': {'d': 5}}

要想深拷贝,还得用 copy.deepcopy()

1
2
3
4
5
6
7
8
9
10
import copy

original_dict = {'a': 1, 'b': [2, 3], 'c': {'d': 4}}
deep_copied_dict = copy.deepcopy(original_dict)

# 现在修改原字典的任何嵌套对象都不会影响 deep_copied_dict
original_dict['b'].append(4)
original_dict['c']['d'] = 5
print(original_dict) # {'a': 1, 'b': [2, 3, 4], 'c': {'d': 5}}
print(deep_copied_dict) # {'a': 1, 'b': [2, 3], 'c': {'d': 4}}