一、可变对象和不可变对象
先来了解两个重要的概念:
- 可变对象:对象的值发生变化,内存地址不变。常见的有list, set, dict
- 不可变对象:对象的值发生变化,内存地址改变。常见的有int, float,string,tuple
为了弄明白赋值,浅拷贝,深拷贝的差异,下面将通过实例来分析。
二、赋值
1、 可变对象
# 可变对象
obj = [1, 2, 3]
a = obj
print("原对象obj:", obj, id(obj))
print("赋值对象a:", a, id(a))
------------------------------------------------------------------------
运行结果:
原对象obj: [1, 2, 3] 2211245785600
赋值对象a: [1, 2, 3] 2211245785600
分析运行结果可知,赋值对象a跟原对象obj的值相同,内存地址相同。
(1)如果对象obj发生改变,赋值的对象a会发生怎样的变化?
# 可变对象
obj = [1, 2, 3]
a = obj
print("原对象obj:", obj, id(obj))
print("赋值对象a:", a, id(a))
print("修改对象obj后:")
obj.append(4)
print("原对象obj:", obj, id(obj))
print("赋值对象a:", a, id(a))
------------------------------------------------------------------------------
运行结果:
原对象obj: [1, 2, 3] 1896687337984
赋值对象a: [1, 2, 3] 1896687337984
修改对象obj后:
原对象obj: [1, 2, 3, 4] 1896687337984
赋值对象a: [1, 2, 3, 4] 1896687337984
分析运行结果可知,原对象obj的值发生改变后,赋值对象a的值也跟着改变了,但是他们的内存地址并不会改变。
2、不可变对象
# 不可变对象
obj = (1, 2, 3)
a = obj
print("原对象obj:", obj, id(obj))
print("赋值对象a:", a, id(a))
---------------------------------------------------------------------------------
运行结果:
原对象obj: (1, 2, 3) 2408442060864
赋值对象a: (1, 2, 3) 2408442060864
分析运行结果可知,赋值对象a跟原对象obj的值相同,内存地址相同。
小结:
赋值只是创建了一个原对象的引用对象,并不会开辟新的内存地址。
原对象发生改变,赋值对象的值也会同步变化,内存地址不变。
三、浅拷贝(copy)
1、可变对象
import copy
# 可变对象
obj = [1, 2, 3]
b = copy.copy(obj)
print("原对象obj:", obj, id(obj))
print("浅拷贝对象b:", b, id(b))
------------------------------------------------------------------------------------
运行结果:
原对象obj: [1, 2, 3] 2723109220736
浅拷贝对象b: [1, 2, 3] 2723109272512
分析运行结果可知,对于可变对象,浅拷贝对象b的内存地址和原对象obj的内存地址不一致,浅拷贝开辟了新的内存地址。
(1)如果对象obj发生改变,浅拷贝对象b会发生怎样的变化?
import copy
# 可变对象
obj = [1, 2, 3]
b = copy.copy(obj)
print("原对象obj:", obj, id(obj))
print("浅拷贝对象b:", b, id(b))
print("修改对象obj后:")
obj.append(4)
print("原对象obj:", obj, id(obj))
print("浅拷贝对象b:", b, id(b))
----------------------------------------------------------------------------
运行结果:
原对象obj: [1, 2, 3] 2065559531904
浅拷贝对象b: [1, 2, 3] 2065559583680
修改对象obj后:
原对象obj: [1, 2, 3, 4] 2065559531904
浅拷贝对象b: [1, 2, 3] 2065559583680
分析运行结果可知,原对象obj的值发生改变后,浅拷贝对象b的值和内存地址并不会受到影像。
(2)如果可变对象obj是一个嵌套结构呢?
import copy
# 可变对象,嵌套结构
obj1 = [1, 2]
obj = [1, 2, 3, obj1]
b = copy.copy(obj)
print("原对象obj:", obj, id(obj))
print("浅拷贝对象b:", b, id(b))
print("修改父对象obj,子对象obj1后:")
obj1.append(3)
obj[0] = 'a'
print("原对象obj:", obj, id(obj))
print("浅拷贝对象b:", b, id(b))
-------------------------------------------------
运行结果:
原对象obj: [1, 2, 3, [1, 2]] 2545761713280
浅拷贝对象b: [1, 2, 3, [1, 2]] 2545761711744
修改父对象obj,子对象obj1后:
原对象obj: ['a', 2, 3, [1, 2, 3]] 2545761713280
浅拷贝对象b: [1, 2, 3, [1, 2, 3]] 2545761711744
分析运行结果可知,对于包含了嵌套结构的可变对象。
父对象obj发生改变后,浅拷贝对象b不会受到影响,值和内存地址都不变。
子对象obj1发生改变后,浅拷贝对象b的子元素[1,2]也跟着变成了[1,2,3],内存地址不变。
小结:
对于可变对象,浅拷贝只拷贝父对象,开辟新的内存地址。不拷贝子对象,只是引用了子对象。
2、不可变对象
import copy
# 不可变对象
obj = (1, 2, 3)
b = copy.copy(obj)
print("原对象obj:", obj, id(obj))
print("浅拷贝对象b:", b, id(b))
----------------------------------------------------------------------------------
运行结果:
原对象obj: (1, 2, 3) 2442259410880
浅拷贝对象b: (1, 2, 3) 2442259410880
分析运行结果可知,对于不可变对象,浅拷贝只是地址的引用,并不会开辟新的内存地址。
四、深拷贝(deepcopy)
1、可变对象
import copy
# 可变对象
obj = [1, 2, 3]
c = copy.deepcopy(obj)
print("原对象obj:", obj, id(obj))
print("深拷贝对象c:", c, id(c))
------------------------------------------------------------------------
运行结果:
原对象obj: [1, 2, 3] 2616267190656
深拷贝对象c: [1, 2, 3] 2616267242368
分析运行结果可知,对于可变对象,深拷贝会开辟新的内存地址。
(1))如果对象obj发生改变,深拷贝对象c会发生怎样的变化?
import copy
# 可变对象
obj = [1, 2, 3]
c = copy.deepcopy(obj)
print("原对象obj:", obj, id(obj))
print("深拷贝对象c:", c, id(c))
print("修改原对象obj后:")
obj[0] = 'a'
print("原对象obj:", obj, id(obj))
print("深拷贝对象c:", c, id(c))
----------------------------------------------------------------------------
运行结果:
原对象obj: [1, 2, 3] 2451928592704
深拷贝对象c: [1, 2, 3] 2451928644480
修改原对象obj后:
原对象obj: ['a', 2, 3] 2451928592704
深拷贝对象c: [1, 2, 3] 2451928644480
分析运行结果可知,原对象obj的值发生改变后,深拷贝对象的值和内存地址并不会受到影像。
(2)如果可变对象obj是一个嵌套结构呢?
import copy
# 可变对象
obj1 = [1,2]
obj = [1, 2, 3,obj1]
c = copy.deepcopy(obj)
print("原对象obj:", obj, id(obj))
print("深拷贝对象c:", c, id(c))
print("修改父对象obj,子对象obj1后:")
obj1.append(3)
obj[0] = 'a'
print("原对象obj:", obj, id(obj))
print("深拷贝对象c:", c, id(c))
--------------------------------------------------------------------------
运行结果:
原对象obj: [1, 2, 3, [1, 2]] 1690664172288
深拷贝对象c: [1, 2, 3, [1, 2]] 1690664170816
修改父对象obj,子对象obj1后:
原对象obj: ['a', 2, 3, [1, 2, 3]] 1690664172288
深拷贝对象c: [1, 2, 3, [1, 2]] 1690664170816
分析运行结果可知,对于包含了嵌套结构的可变对象。
父对象obj发生改变后,深拷贝对象c不会受到影响,值和内存地址都不变。
子对象obj1发生改变后,深拷贝对象c不会受到影响,值和内存地址都不变。
小结:
对于可变对象,深拷贝会拷贝父对象和所有的子对象,开辟新的内存地址。
2、不可变对象
import copy
# 不可变对象
obj = (1,2,3)
c = copy.deepcopy(obj)
print("原对象obj:", obj, id(obj))
print("深拷贝对象c:", c, id(c))
------------------------------------------------------------------------------
运行结果:
原对象obj: (1, 2, 3) 2928533385024
深拷贝对象c: (1, 2, 3) 2928533385024
分析运行结果可知,对于不可变对象,深拷贝只是地址的引用,并不会开辟新的内存地址。
五、总结
赋值:
赋值只是创建了一个原对象的引用对象,并不会开辟新的内存地址。
原对象发生改变,赋值对象的值也会同步变化,内存地址不变。
浅拷贝:
对于可变对象,浅拷贝只拷贝父对象,开辟新的内存地址。不拷贝子对象,只是引用了子对象。
对于不可变对象,浅拷贝只是地址的引用,并不会开辟新的内存地址。
深拷贝:
对于可变对象,深拷贝会拷贝父对象和所有的子对象,开辟新的内存地址。
对于不可变对象,深拷贝只是地址的引用,并不会开辟新的内存地址。