赋值
在python中,赋值仅仅是复制了对象的引用,并没有开辟内存空间
a = 1
b = a
上述代码只是把a的引用复制给了b,结果是a和b同时指向1
对于可变对象
a = [1, 2, 3]
b = a
b.append(4)
结果a和b都变成了 [1, 2, 3, 4]
浅拷贝(shadow copy)
浅拷贝会创建新对象,其内容不是原对象本身的引用,而是原对象内第一层对象的引用。
浅拷贝有三种形式
切片操作
b = a[:]
或者b = [x for x in a]
;
a = [1, 2, ['a', 'b', [10, 20]]]
# 切片,b的第一层对象与a的第一层对象指向一致
b = a[:]
print(a) # [1, 2, ['a', 'b', [10, 20]]]
print(b) # [1, 2, ['a', 'b', [10, 20]]]
print(id(a[2])) # 1660758488584
print(id(b[2])) # 1660758488584
a[2]和b[2]的id相同,说明它们指向同一个内存地址
给a的第一层对象a[2]添加一个值
a[2].append('c')
print(a) # [1, 2, ['a', 'b', [10, 20], 'c']]
print(b) # [1, 2, ['a', 'b', [10, 20], 'c']]
print(id(a[2])) # 1660758488584
print(id(b[2])) # 1660758488584
由于a[2]是列表,是容器类对象,所以添加值后id不会变
对a的第一层对象a[2]进行修改
a[2] = ['c', 'd']
print(a) # [1, 2, ['c', 'd']]
print(b) # [1, 2, ['a', 'b', [10, 20], 'c']]
print(id(a)) # 2911387104264
print(id(b)) # 2911387106504
print(id(a[2])) # 1660758488712
print(id(b[2])) # 1660758488584
可以看到a和b是两个不同的对象,对a[2]进行赋值,把a[2]指向另一个对象,因此a和b就不同了
对a的第二层对象进行添加操作
a[2][2].append(30)
print(a) # [1, 2, ['a', 'b', [10, 20, 30]]]
print(b) # [1, 2, ['a', 'b', [10, 20, 30]]]
print(id(a[2])) # 1721086473736
print(id(b[2])) # 1721086473736
print(id(a[2][2])) # 1721086473800
print(id(b[2][2])) # 1721086473800
添加操作仍然不会改变id
对a的第二层对象进行修改操作
a[2][2] = ['love', 'sweet']
print(a) # [1, 2, ['a', 'b', ['love', 'sweet']]]
print(b) # [1, 2, ['a', 'b', ['love', 'sweet']]]
print(id(a[2])) # 2389899703816
print(id(b[2])) # 2389899703816
print(id(a[2][2])) # 2389899703944
print(id(b[2][2])) # 2389899703944
工厂函数
b = list(a)
copy 函数
b = copy.copy(a)
小结
浅拷贝特点
- 重新创建对象
- 内容是第一层对象的引用
- 修改第一层对象不会相互影响,修改第二层及以上会相互影响
深拷贝
深拷贝只有一种形式,copy.deepcopy()
深拷贝和浅拷贝对应,深拷贝拷贝了对象的所有元素,包括多层嵌套的元素。因此,它的时间和空间开销要高。
深拷贝特点
- 完全拷贝,新对象与原对象没有一点关系
- 开销大
注意:对于数字、字符串没有拷贝一说,只有对象的引用