""" 一、赋值在python中就是简单的对象引用 """ list_a = ["aaa", "bbb"] list_b = list_a print(id(list_a), id(list_b)) # 输出 2127728239240 2127728239240 # 通过上面操作可以看出,list_b和list_a指向同一片内存,list_b不过是list_a的别名,是引用,除了list_b这个名字以外,没有其它的内存开销。。 # 修改了list_a,就影响了list_b;同理,修改了list_b就影响了list_a list_b[1] = "ccc" print(f"list_a = {list_a}") # 输出 list_a = ['aaa', 'ccc'] print(f"list_b = {list_b}") # 输出 list_b = ['aaa', 'ccc'] """ 二、浅拷贝
在浅拷贝时,拷贝出来的新对象的地址和原对象是不一样的,但是新对象里面的可变元素(如列表)的地址和原对象里的可变元素的地址是相同的,也就是说浅拷贝它拷贝的是浅层次的数据结构(不可变元素),对象里的可变元素作为深层次的数据结构并没有被拷贝到新地址里面去,
而是和原对象里的可变元素指向同一个地址,所以在新对象或原对象里对这个可变元素做修改时,两个对象是同时改变的。
""" list_c = ["aaa", "bbb"] list_d = list_c.copy() print(id(list_c), id(list_d)) # 输出 1803491942920 1803491976008 # 通过id可以看出,发现它们也不指向同一片内存,也就是说浅拷贝产生的list_d不再是list_c了,使用is也可以发现他们不是同一个对象。 for x in list_c: print(id(x)) # 输出 "aaa" = 1803491959280 "bbb" = 1803491948000 for y in list_d: print(id(y)) # 输出 "aaa" = 1803491959280 "bbb" = 1803491948000 # 但是当我们查看元素的id时,可以看到两个对象包含的元素的地址是相同的。 list_d[1] = "ccc" print(f"list_c = {list_c}") # 输出 list_c = ['aaa', 'bbb'] print(f"list_d = {list_d}") # 输出 list_d = ['aaa', 'ccc'] # 在这种情况下,list_c和list_d是不同的对象,修改list_d理论上不会影响list_c。 list_e = [["aaa", "bbb"], "ccc"] list_f = list_e.copy() list_f[0][1] = "ccc" print(f"list_e = {list_e}") # 输出 list_e = [['aaa', 'ccc'], 'ccc'] print(f"list_f = {list_f}") # 输出 list_e = [['aaa', 'ccc'], 'ccc'] # 但是要注意,浅拷贝之所以称为浅拷贝,是它仅仅只拷贝了一层,在list_a中有一个嵌套的list,如果我们修改了它,情况就不一样了。 # 通过list_f[0][1] = "ccc" 修改list_f后,将发现list_e也发生了变化。这是因为,修改嵌套的元素的内存地址是一样,与前面赋值原理一样。
""" 三、深拷贝 深拷贝只有一种形式,copy模块中的deepcopy函数。 和浅拷贝对应,深拷贝拷贝了对象的所有元素,包括多层嵌套的元素。因而,它的时间和空间开销要高。 同样对list_a,若使用list_b = copy.deepcopy(list_a),再修改list_b将不会影响到list_a了。即使嵌套的列表具有更深的层次,也不会产生任何影响。 因为深拷贝出来的对象根本就是一个全新的对象,不再与原来的对象有任何关联。 """ """ ### 关于拷贝的警告 1、对于非容器类型,如数字,字符,以及其它“原子”类型,没有拷贝一说。产生的都是原对象的引用。 2、如果元组变量值包含原子类型对象,即使采用了深拷贝,也只能得到浅拷贝。 """
四、浅拷贝与深拷贝中的注意点
- 列表的切片以及字典的copy方法都是浅拷贝
- 如果对一个全部都是不可变类型的数据用copy.copy()或copy.deepcopy()进行拷贝,他们结果相同,都是引用指向。
- 如果拷贝的是一个拥有可变类型的数据,即使元组是最顶层,那么deepcopy依然是 深拷贝,而copy.copy()拷贝的还是指向