Python_赋值、浅拷贝和深拷贝
一、赋值
赋值在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']
二、浅拷贝
浅拷贝,即拷贝生成的新对象的内存地址和原对象不一样,但是新对象里的可变元素(如列表)的内存地址和原对象里的可变元素的内存地址是相同的。也就是说,浅拷贝仅仅复制原对象浅层次(第一层)的数据结构,对象里的可变元素作为深层次的数据结构并没有在拷贝时生成新的内存地址,而是和原对象里的可变元素指向同一个内存地址,所以在新对象或原对象里对这个可变元素做修改时,两个对象的数据会同时改变。
# 【1、原对象包含不可变元素】 list_c = ["aaa", "bbb"] list_d = list_c.copy() print(id(list_c), id(list_d)) # 输出 1803491942920 1803491976008 # 通过id可以发现它们指向不同的内存地址,也就是说浅拷贝产生的list_d是一个新对象。 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,这是因为修改的是对象的第一层数据。 # 【2、原对象包含可变元素】 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_f[0][1] = "ccc" ,发现list_e也发生了变化。这是因为嵌套的元素的内存地址是一样,与前面赋值原理一样。
三、深拷贝
与浅拷贝相比,深拷贝将原对象所有元素进行了拷贝,包括多层嵌套的元素。因此,深拷贝花费的时间和内存比浅拷贝要高。
import copy list_e = [["aaa", "bbb"], "ccc"] list_f = copy.deepcopy(list_e) list_f[0][1] = "ccc" # 修改嵌套的可变元素值 print(f"list_e = {list_e}") # 输出 list_e = [['aaa', 'bbb'], 'ccc'] print(f"list_f = {list_f}") # 输出 list_e = [['aaa', 'ccc'], 'ccc'] # 修改深拷贝生成的新对象的嵌套可变元素,可以发现不会再对原对象再有影响
四、拷贝注意点
1、对于非容器类型,如数字,字符,以及其它“原子”类型,没有拷贝一说。产生的都是原对象的引用。
2、如果元组变量值包含原子类型对象,即使采用了深拷贝,也只能得到浅拷贝。
3、列表的切片以及字典的copy方法都是浅拷贝