浅拷贝: 浅拷贝后,两个对象的地址不相同,所以直接改变原始对象,是不会影响拷贝对象的;但是其中的元素地址是分别对应相同的,所以改变原始对象中为可变类型元素的值,会同时影响拷贝对象,而改变原始对象中为不可变类型元素的值,不会影响拷贝对象。(copy.copy(),a2 = a1.copy() 都属于浅拷贝)
import copy
list1 = [[1,2], 'a']
# list1.copy()结果一样
list2 = copy.copy(list1) # 浅拷贝
# 两个对象地址不相同
print(id(list1))
print(id(list2))
# 对象对应的元素地址是相同的
print(id(list1[0]))
print(id(list2[0]))
print(id(list1[1]))
print(id(list2[1]))
# 因为list1中第一个元素是可变类型,改变其中的第一个值,copy对象也会改变
list1[0][0] = 8
print(list1)
print(list2)
# 因为list1中第二个元素是不可变类型,改变其中的值,copy对象不会改变
list1[1] = 'f'
print(list1)
print(list2)
# 直接改变原始对象,是不会影响拷贝对象的
list1.append(6)
print(list1)
print(list2)
输出:
31190216
42317320
31178568
31178568
31013104
31013104
[[8, 2], 'a']
[[8, 2], 'a']
[[8, 2], 'f']
[[8, 2], 'a']
[[8, 2], 'f', 6]
[[8, 2], 'a']
而对于 a2 = a1,两个对象的所有地址都对应相同,且a1发生任何改变对a2都有影响
list1 = [[1,2], 'a']
list2 = list1
# 两个对象地址相同
print(id(list1))
print(id(list2))
# 对象的元素地址也相同
print(id(list1[0]))
print(id(list2[0]))
print(id(list1[1]))
print(id(list2[1]))
# list1发生改变,list2也受影响
list1[0][0] = 8
print(list1)
print(list2)
list1[1] = 'f'
print(list1)
print(list2)
list1.append(6)
print(list2)
输出:
3822536
3822536
3822024
3822024
31013104
31013104
[[8, 2], 'a']
[[8, 2], 'a']
[[8, 2], 'f']
[[8, 2], 'f']
[[8, 2], 'f', 6]
深拷贝: 深拷贝,除了两个对象中不可变类型元素的地址相同以外,其他全部不同,且拷贝对象不受原始对象的任何改变而影响(copy.deepcopy())
import copy
list1 = [[1,2], 'a']
list2 = copy.deepcopy(list1) # 深拷贝
# 两个对象地址不相同
print(id(list1))
print(id(list2))
# 对象的可变类型元素地址不相同
print(id(list1[0]))
print(id(list2[0]))
# 对象的不可变类型元素地址相同
print(id(list1[1]))
print(id(list2[1]))
# 改变原始对象的元素,不会对复制对象有影响
list1[0][0] = 8
print(list1)
print(list2)
list1[1] = 'f'
print(list1)
print(list2)
list1.append(6)
print(list1)
print(list2)
输出:
32685960
31934664
31309640
31934792
31144176
31144176
[[8, 2], 'a']
[[1, 2], 'a']
[[8, 2], 'f']
[[1, 2], 'a']
[[8, 2], 'f', 6]
[[1, 2], 'a']
列表内存自动管理功能: 在删除列表中的元素时,python会自动对列表内存进行收缩,并移动列表中的元素以保证元素之间没有间隙,所以正序删除列表中元素时,被删元素后面的值会向前顶,然后导致漏删。倒序删除元素时,被删元素前面的值不会向后靠,所以可以完整的遍历到列表中所有的元素。
data = [1, 2, 3]
for i in data:
data.remove(i)
print(data) # [2]
为什么没有得到空列表?
原列表在内存中为:
第一次执行到 data.remove(i) 时,将第一个元素 1 删除,列表变为:
特别注意:这里删除前面的1,后面的2,3都会往前顶,内存收缩
所以第二次执行 data.remove(i) 时,就将第二个元素 3 删除,列表变为:
此时列表已经没有第三个元素了,即退出循环
如何解决这个问题呢?
方法一:倒序删除(从后往前遍历,每次循环删除最后一个元素,列表中的元素就不会移动,造成漏删)
data = [1, 2, 3]
for i in data[: : -1]:
data.remove(i)
print(data) # []
data = [1, 2, 3]
for i in reversed(data):
data.remove(i)
print(data) # []
data = [1, 2, 3]
data.reverse() # 改变原列表倒序
for i in data:
data.remove(i) # 相当于还是从前往后删除
print(data) # [2]
方法二:再定义一个data02,通过遍历data02,来删除data中对应的元素,这也不会导致遍历漏掉
data = [1, 2, 3]
data2 = [1, 2, 3]
for i in data2:
data.remove(i)
print(data) # []
方法三:拷贝一个data(原理类似方法二)
前面提到,不管是深拷贝还是浅拷贝,两个对象的地址是不一样的,所以删除 data 中的元素,都是是对data2没有影响的,而 data2 = data ,两个对象的地址是一样的,删除 data 中的元素,对 data2 是有影响的。
import copy
data = [1, 2, 3]
# 深拷贝,改变data对data2没有影响,即对遍历没有影响
data2 = copy.deepcopy(data)
for i in data2:
data.remove(i)
print(data) # []
data = [1, 2, 3]
data2 = copy.copy(data) # data.copy()一样
for i in data2:
data.remove(i)
print(data) # []
data = [1, 2, 3]
data2 = data # data的任何改变data2都受影响,所以遍历还是会漏删
for i in data2:
data.remove(i)
print(data) # [2]