Python标签
res = []
def func(obj):
res.append(obj)
a = [1, 2, 3]
print(id(a)) # 2897774181960
func(a)
print(id(res[0])) # 2409845052424
print("res: ", res)
a.append(4)
print("res: ", res)
res: [[1, 2, 3]]
res: [[1, 2, 3, 4]]
从输出可以看到,a和res[0]的id一样,说明指向了同一块内存空间,所以a改变了之后res[0]也跟着改变了,你以为拷贝了,其实并没有
原因:
Python没有变量,我们平时所说的变量其实只是标签,是引用。
执行:values=[0,1,2]的时候,python做的事情是首先创建一个列表对象[0,1,2],然后给它贴上名为values的标签。如果随后执行values=[3,4,5]的话,python做的事情是创建另一个列表对象[3,4,5],然后把刚才那张名为values的标签从前面的[0,1,2]对象上撕下来,重新贴到[3,4,5]这个对象上。
所以,这里的a和res[0]就相当于两张标签贴到了同一个对象[1,2,3]上
浅拷贝
解决办法是用拷贝,这里先说浅拷贝shallow copy
注意:[:]生成对象的拷贝或者是复制序列,不再是引用和共享变量,但此法只能顶层复制
res = []
def func(obj):
res.append(obj[:]) # 这里不一样
a = [1, 2, 3]
print(id(a)) # 1558831191048
func(a)
print(id(res[0])) # 1558832493512
print("res: ", res)
a.append(4)
print("a: ", a)
print("res: ", res)
res: [[1, 2, 3]]
a: [1, 2, 3, 4]
res: [[1, 2, 3]]
可以看到a和res[0]的id不一样了,a改变了之后,res[0]没有改变,对于这种场景,浅拷贝就适用了,但如果包含子对象,浅拷贝就有问题了
a = [0, [1, 2], 3]
b = a[:]
a[0] = 8
a[1][1] = 9
print(a) # [8, [1, 9], 3]
print(b) # [0, [1, 9], 3]
可以看到,b[1][1]也跟着改变了,这就是浅拷贝只做顶层拷贝的效果
原因从图里就可以看出来了,a[1]和b[1]还是指向了同一个对象
深拷贝
解决办法使用深拷贝
import copy
a = [0, [1, 2], 3]
b = copy.deepcopy(a)
print(id(a[1])) # 2071720571784
print(id(b[1])) # 2071721298184
a[0] = 8
a[1][1] = 9
print(a) # [8, [1, 9], 3]
print(b) # [0, [1, 2], 3]
深拷贝之后就指向两个完全不同的对象了
对象也是一样
import copy
class Person:
def __init__(self):
self.num = [1, 2]
p1 = Person()
print(p1.num) # [1, 2]
p2 = p1 # 指向同一个内存
print(p2.num) # [1, 2]
p3 = copy.deepcopy(p1) # 深拷贝
print(p3.num) # [1, 2]
p1.num.append(3)
print(p1.num) # [1, 2, 3]
print(p2.num) # [1, 2, 3] 也改变了
print(p3.num) # [1, 2] 没有改变