python的赋值号
- python的赋值符号会创建一个新的对象出来,并与之关联,无论其是不是可变类型。
x = 666
print(id(x))
x = 233
print(id(x))
y = [1,2,3]
print(id(y))
y = [1,2,4]#在这里,即使是y=[1,2,3]内存地址也会改变
print(id(y))
4369996880
4297155952
4369898120
4369862216
如上代码所示,无论是不可变类型还是可变类型,赋值号总是会创建一个新的对象并与变量名关联起来,而不是修改原来引用的值。
- python的赋值号会直接创建引用,而不是赋值一个对象。这就是为什么我们要讨论深/浅拷贝的原因。
x = [1,2,3,4]
y = x
y[0] = 233 #改变了x,对y[0]=233等价于x[0]=233,即创建了一个新的对象233,并让x[0]指向它
y.append(666) #改变了x,可变对象的函数不会创建一个新对象,而是直接在原对象的内存上进行修改
#(若x,y是不可变对象string,则y的函数将创建一个新对象,x不会变)
x
[233, 2, 3, 4, 666]
上述代码显示了y是x的引用,因此对y进行修改的时候,x也改变了。
不过要注意,如果你这样写代码,则x不会改变。
x = [1,2,3,4]
y = x
y = [1,2]#因为赋值号重新创建了新的对象,并让y指向新对象,因此x不会改变。
x
[1, 2, 3, 4]
python的浅拷贝
为了拷贝出一个对象,可以用函数copy.copy()或者对于可索引对象直接[:]。可以看到,copy出来的对象修改之后,不会影响原对象。
import copy
x = [1,2,3]
y = copy.copy(x)
y.append(4)
print(x,y)
z = x[:]
z.append(666)
print(x,z)
z[0] = 233
print(x,z)
[1, 2, 3] [1, 2, 3, 4]
[1, 2, 3] [1, 2, 3, 666]
[1, 2, 3] [233, 2, 3, 666]
然而...
x = [1,[2,3]]
y = copy.copy(x)#或者y=x[:]
y[1].append(4)
print(x,y)
y[1][0] = 666
print(x,y)
[1, [2, 3, 4]] [1, [2, 3, 4]]
[1, [666, 3, 4]] [1, [666, 3, 4]]
浅拷贝彻底崩溃,对拷贝后的对象作修改依旧改变了原对象。这是为什么呢?在最后我会说明深浅拷贝的原理。先说解决方法:深拷贝。
python的深拷贝
当然,深拷贝可以解决这些问题。
x = [1,[2,3]]
y = copy.deepcopy(x)
y[1].append(4)
print(x,y)
y[1][0] = 666
print(x,y)
[1, [2, 3]] [1, [2, 3, 4]]
[1, [2, 3]] [1, [666, 3, 4]]
至于原因,简单来说,浅拷贝创造了一个新的对象,但是新对象里也全部都是老对象里面子结构的引用。因此经过浅拷贝之后,通过赋值号来改变新内容当然不会改变原内容,但是一旦子结构中存在可变类型的对象,那么修改这些可变类型对象的值当然就会影响原内容。
而深拷贝与浅拷贝唯一的不同仅仅是在深拷贝时,遇到了原内容中的可变类型的子结构,不会直接创建一个引用指向它,而是重新创建一个新的对象并指向新创建的对象,并递归的进行深拷贝。
听的有点懵?下面的代码将会直观的展示深拷贝与浅拷贝的区别。
x = ['a',['b',12]]
y = copy.copy(x)
z = copy.deepcopy(x)
print(id(x),id(x[0]),id(x[1]))
print(id(y),id(y[0]),id(y[1]))
print(id(z),id(z[0]),id(z[1]))
4369860232 4337588016 4369859592
4370811144 4337588016 4369859592
4370247176 4337588016 4370766728
上述代码完美的展示了,深浅拷贝均创造了一个新的对象,因此id(x),id(y),id(z)是不一样的。然后浅拷贝将其中的子结构完全引用了原数据,而深拷贝则仅仅在不可变类型的数据上直接引用原数据,而可变类型将会创造新的对象。因此id(x[1])==id(y[1])!=id(z[1])
EOF