我们在使用列表时,可能会遇到这种情况:使用list.append()直接操作于原列表时,当原列表中的元素发生了变化,导致我们的新列表也对应地发生改变。举例如下:
student = ['Mary', 'David', 'Jack', 'Rose']
result = []
result.append(student)
print('student:', student) # student: ['Mary', 'David', 'Jack', 'Rose']
print('result:', result) # result: [['Mary', 'David', 'Jack', 'Rose']]
现在对 student 做一个 pop操作,删除最后一个元素:
student.pop(-1)
print('student:', student) # student: ['Mary', 'David', 'Jack']
print('result:', result) # result: [['Mary', 'David', 'Jack']]
根据输出结果我们可以发现,student列表pop出最后一个元素后,result 列表也对应地发生了改变。
那么,如果我们希望对 student 做操作时,result 列表不受影响该怎么办呢?
这时候我们可以对列表进行拷贝。
1. 拷贝
对 student 列表进行 深拷贝后,再进行append操作。
import copy
student = ['Mary', 'David', 'Jack', 'Rose']
student_copy = copy.deepcopy(student) #对象拷贝,深拷贝
result = []
result.append(student_copy)
print('student:', student) # student: ['Mary', 'David', 'Jack', 'Rose']
print('student_copy:', student_copy) # student_copy: ['Mary', 'David', 'Jack', 'Rose']
print('result:', result) # result: [['Mary', 'David', 'Jack', 'Rose']]
对 student 做一个 pop操作,删除最后一个元素:
student.pop(-1)
print('student:', student) # student: ['Mary', 'David', 'Jack']
print('student_copy:', student_copy) # student_copy: ['Mary', 'David', 'Jack', 'Rose']
print('result:', result) # result: [['Mary', 'David', 'Jack', 'Rose']]
可以发现拷贝之后,新列表中的元素不受原列表影响。
1.1 浅拷贝
浅拷贝是在复制的时候只增加了一个指针,没有给其分配了内存空间,即你原来有一个a列表,你这个a里的值都有指向自己的指针,而且也有自己的内存空间a1,当浅拷贝a列表的时候得到一个A,这个A里的值都有指向自己的指针,但是他的的内存空间还是a1;这个时候你对无论是a还是A进行操作,都会改变内存空间a1里的值。
1.2 深拷贝
深拷贝是在复制的时候不但增加了一个指针,而且还给其分配了内存空间,即你原来有一个a列表,你这个a里的值都有指向自己的指针,而且也有自己的内存空间a1,当深拷贝a列表的时候得到一个A,这个A里的值都有指向自己的指针,而且也有自己的内存空间A1;那么你再对原来的进行操作的时候可以去a里去寻找,在A里进行操作,由于都有自己的独立的内存空间,那么不会相互影响。
1.3 两者区别
那么深拷贝和浅拷贝之间又有什么区别呢?我们通过以下的例子进行说明:
import copy
a = [1,2,3,4,5,['a','b']] # 原始对象
b = a # 赋值,传对象的引用
c = copy.copy(a) # 对象拷贝,浅拷贝
d = copy.deepcopy(a) # 对象拷贝,深拷贝
print("a:", a, " id(a)=", id(a), " id(a[5])=", id(a[5]))
print('b:', b, " id(b)=", id(b), " id(b[5])=", id(b[5]))
print("c:", c, " id(c)=", id(c), " id(c[5])=", id(c[5]))
print("d:", d, " id(d)=", id(d), " id(d[5])=", id(d[5]))
输出结果如下:
a: [1, 2, 3, 4, 5, ['a', 'b']] id(a)= 140442974925704 id(a[5])= 140442975469896
b: [1, 2, 3, 4, 5, ['a', 'b']] id(b)= 140442974925704 id(b[5])= 140442975469896
c: [1, 2, 3, 4, 5, ['a', 'b']] id(c)= 140443266897480 id(c[5])= 140442975469896
d: [1, 2, 3, 4, 5, ['a', 'b']] id(d)= 140442974915272 id(d[5])= 140443241576520
进一步修改列表中的元素:
a.append(6) #修改对象a
a[5].append('c') #修改对象a中的['a','b']数组对象
print("a:", a, " id(a)=", id(a), " id(a[5])=", id(a[5]))
print('b:', b, " id(b)=", id(b), " id(b[5])=", id(b[5]))
print("c:", c, " id(c)=", id(c), " id(c[5])=", id(c[5]))
print("d:", d, " id(d)=", id(d), " id(d[5])=", id(d[5]))
输出结果如下:
a: [1, 2, 3, 4, 5, ['a', 'b', 'c'], 6] id(a)= 140442974925704 id(a[5])= 140442975469896
b: [1, 2, 3, 4, 5, ['a', 'b', 'c'], 6] id(b)= 140442974925704 id(b[5])= 140442975469896
c: [1, 2, 3, 4, 5, ['a', 'b', 'c']] id(c)= 140443266897480 id(c[5])= 140442975469896
d: [1, 2, 3, 4, 5, ['a', 'b']] id(d)= 140442974915272 id(d[5])= 140443241576520
从程序的结果来看:
- 列表a和b是赋值操作,两个对象完全指向同一个地址,a和b就是同一块地址的两个引用,其实就是一个东西,所以一个对象在修改浅层元素(不可变)或深层元素(可变)时,另一个对象也同时在变;
- c是a进行浅拷贝生成的对象,可以看到a(或b)和c两个对象整体的id是不同的,但是里面的第5个元素的地址却是相同的(指向同一个地址)。所以b在浅层次元素层面(不可变)增加一个元素时,c并没跟着增加,但是b的第5个元素在增加一个元素时,c的第5个元素也跟着增加了,这就是因为b和c的第5个元素-列表是指向同一个地址的,这个地址上的值变了,在两个地方会同时改变。
- 再看d,d的浅层次元素(不可变)和 深层次元素(可变)的地址和a,b,c都不一样,所以,a,b,c无论怎么修改,d都不会跟着改变,这就是深拷贝的结果。
也可以这样理解:
- 深拷贝就是完全跟以前就没有任何关系了,原来的对象怎么改都不会影响当前对象
- 浅拷贝,原对象的list元素改变的话会改变当前对象,如果当前对象中list元素改变了,也同样会影响原对象。
通常复制的时候要用深拷贝,因为浅拷贝后,两个对象中不可变对象指向不同地址,相互不会改变,但是两个对象中的可变元素是指向相同的地址,一个变了,另一个会同时改变,会有影响(list是可变对象)。
如果要让原list和copy list没有影响怎么办?
用深拷贝,拷贝后完全开辟新的内存地址来保存之前的对象,虽然可能地址执行的内容可能相同,但是不会相互影响。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/162846.html