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] 没有改变