Python 直接赋值及浅拷贝和深拷贝学习与理解
一、含义区别
Python里对直接赋值、浅拷贝及深拷贝的直接定义是这样的:
直接赋值:将一对象赋值给一变量时,并未拷贝该对象,只是拷贝了该对象的引用(别名);
浅拷贝:浅拷贝是指使用copy,拷贝父对象,不会拷贝对象内部的子对象;
深拷贝:深拷贝是指使用deepcopy,是copy模块的deepcopy方法,完全拷贝了父对象及子对象。
从上述文字还是很难直观的理解这三者之间的具体含义,接下来还是通过一些实验结合理论来加深认识。
二、验证测试
2.1 直接赋值
对于直接赋值的理解,可通过如下的测试来加深理解。
# 直接赋值测试
list01 = [1,2,3,['a','b','c']]
list02 = list01
print("list01初始值是{}:",list01,";","\t","list01初始地址是:",id(list01))
print("list02初始值是{}:",list02,";","\t","list02初始地址是:",id(list02))
print("\n")
# 测试为list01 追加一个值
list01.append(['d','e'])
print("list01追加值后是{}:",list01,";","\t","list01追加值后地址是:",id(list01))
print("list02追加值后是{}:",list02,";","\t","list02追加值后地址是:",id(list02))
以上测试输出结果如下图所示:
从上述测试可看到直接赋值,两个变量其实指向同一个地址,当一个变量追加值后,被直接赋值的另一变量也同样指向同一地址。
为更便于直观理解,我也在网上找了一幅图,用图来描述直接赋值的含义。
b = a, b和a都指向同一对象。
2.2 浅拷贝(copy)
对于浅拷贝的理解,可通过如下测试来加深理解。
# 浅拷贝
list01 = [1,2,3,['a','b','c']]
list02 = copy.copy(list01)
print("list01初始值是{}:",list01,";","\t","list01初始地址是:",id(list01))
print("list02初始值是{}:",list02,";","\t","list02初始地址是:",id(list02))
print("\n")
print("修改前:list01[3]子对象值是{}:",(list01[3]),";","\t","修改前:list01[3]子对象地址是:",id(list01[3]))
print("修改前:list02[3]子对象值是{}:",(list02[3]),";","\t","修改前:list02[3]子对象地址是:",id(list02[3]))
print("\n")
# 测试为list01 追加一个值
list01.append(5)
print("list01追加值后是{}:",list01,";","\t","list01追加值后地址是:",id(list01))
print("list02追加值后是{}:",list02,";","\t","list02追加值后地址是:",id(list02))
print("\n")
# 测试为list01的子对象列表追加一个值
list01[3].append('d')
print("list01子对象列表追加一新值后值是{}:",list01,";","\t","list01子对象列表追加一新列表后地址是:",id(list01))
print("list02在list01子对象列表追加新值后值是{}:",list02,";","\t","list02在list01子对象列表追加新值后地址是:",id(list02))
print("\n")
print("修改后:list01[3]子对象值是{}:",(list01[3]),";","\t","修改后:list01[3]子对象地址是:",id(list01[3]))
print("修改后:list02[3]子对象值是{}:",(list02[3]),";","\t","修改后:list02[3]子对象地址是:",id(list02[3]))
以上测试输出结果如下图所示:
执行copy拷贝,list01和list02分别是一独立对象,但它们的子对象还是指向同一对象地址。
针对浅拷贝,我也从网上找到一幅图,通过图来更直观的理解浅拷贝。
浅拷贝, a 和 b 是一个独立的对象,但他们的子对象还是指向同一对象。
注:在执行copy时,拷贝list01的子列表,其实是拷贝了该子列表list01[3]的一个整体引用地址值。
2.3 深拷贝(deepcopy)
对于深拷贝的理解,可通过如下测试来加深理解。
# 浅拷贝
list01 = [1,2,3,['a','b','c']]
list02 = copy.deepcopy(list01)
print("list01初始值是{}:",list01,";","\t","list01初始地址是:",id(list01))
print("list02初始值是{}:",list02,";","\t","list02初始地址是:",id(list02))
print("\n")
print("修改前:list01[3]子对象值是{}:",(list01[3]),";","\t","修改前:list01[3]子对象地址是:",id(list01[3]))
print("修改前:list02[3]子对象值是{}:",(list02[3]),";","\t","修改前:list02[3]子对象地址是:",id(list02[3]))
print("\n")
# 测试为list01 追加一个值
list01.append(5)
print("list01追加值后是{}:",list01,";","\t","list01追加值后地址是:",id(list01))
print("list02追加值后是{}:",list02,";","\t","list02追加值后地址是:",id(list02))
print("\n")
# 测试为list01的子对象列表追加一个值
list01[3].append('d')
print("list01子对象列表追加一新值后值是{}:",list01,";","\t","list01子对象列表追加一新列表后地址是:",id(list01))
print("list02在list01子对象列表追加新值后值是{}:",list02,";","\t","list02在list01子对象列表追加新值后地址是:",id(list02))
print("\n")
print("修改后:list01[3]子对象值是{}:",(list01[3]),";","\t","修改后:list01[3]子对象地址是:",id(list01[3]))
print("修改后:list02[3]子对象值是{}:",(list02[3]),";","\t","修改后:list02[3]子对象地址是:",id(list02[3]))
以上测试输出结果如下图所示:
执行deepcopy拷贝,list01和list02分别是一独立对象,原始对象的改变不会造成深拷贝里任何子元素的改变。
针对深拷贝,我也从网上找到一幅图,通过图来更直观的理解浅拷贝。
深度拷贝, a 和 b 完全拷贝了父对象及其子对象,两者是完全独立的
三、总结
浅拷贝:只能copy列表的一级元素,复制了嵌套的可变数据类型的地址,
深拷贝:能copy列表的所有层级的元素,复制了嵌套的可变数据类型元素。
注:[可变数据类型是指列表中的子对象列表,因为列表是可变长度]
四、应用场景
浅拷贝(copy)和深拷贝(deepcopy)都可以用到哪些日常应用场景里呢:
1) 当处理中间结果时往往不想对原始数据进行修改,此时可使用深拷贝(deepcopy);
当只是希望新增一辅助列(只对父对象进行数据修改),此时可使用浅拷贝(copy),以便节约系统内存;