Python深拷贝和浅拷贝 - 解决循环删除列表中的元素出错问题

浅拷贝和深拷贝区别

浅拷贝: 浅拷贝后,两个对象的地址不相同,所以直接改变原始对象,是不会影响拷贝对象的;但是其中的元素地址是分别对应相同的,所以改变原始对象中为可变类型元素的值,会同时影响拷贝对象,而改变原始对象中为不可变类型元素的值,不会影响拷贝对象。(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]

为什么没有得到空列表?

原列表在内存中为:
Python深拷贝和浅拷贝 - 解决循环删除列表中的元素出错问题_第1张图片
第一次执行到 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]

你可能感兴趣的:(Python,python)