众所周知,Python的赋值和一般的高级语言的赋值有很大的不同,它是引用赋值。看下面的代码1:
>>>a = 5
>>>b = 8
>>> a = b #结果如下图1:
图1
开始的时候a指向的是5,b指向的是8,当a = b的时候,b把自己指向的地址(也就是8的内存地址)赋给了a,那么最后的结果就是a和b同时指向了8。我们可以用Python内建的函数id()来测试
那好,现在我们来看下一种情况:
>>>a = [1,2,3]
>>>id(a ) #可以得到1217620
>>>a.append(4) #这时候a = [1,2,3,4],
>>>id(a) #结果还是1217620,证明如果是添加对象,不改变变量的内存地址,这个是我们下面要探讨的问题的先决条件。
>>>b = a # b = [1,2,3,4]
>>>a.append(5) #a = b = [1,2,3,4,5]
如果没有上面的结论,[1,2,3,4]的地址和[1,2,3]的地址不一样,那么最终的结果是 a = [1,2,3,4,5], b = [1,2,3,4]
那怎么才能够让a变成[1,2,3,4],而b仍然是[1,2,3]呢? 这个时候我们需要用到copy,也就是拷贝,python内置了copy模块。看代码2
>>>import copy
>>>a = [1,2,3]
>>>b = copy.copy(a) #这个时候a = b = [1,2,3]
>>>a.append(4) # a = [1,2,3,4],b = [1,2,3]
事情到这里好像就结束了,但是问题又来啦,看下面的代码3
>>>import copy
>>>a = [1,2,[3,4]]
>>>b = copy.copy(a)
>>>id(a) #12177080
>>>id(b) #12186888,证明a、b指向的不是同一个地址
>>>a[2].append(5) # 这时候a = b = [1,2,[3,4,5]],怎么会这样????
>>>id(a[0]) # 100380032
>>>id(b[0]) # 100380032 二者是共享地址的
也就是说这个copy.copy复制的不彻底,只复制了对象,对象的属性却是共享的。那我们有别的方法没?答案是肯定的,看下面的代码4
>>>import copy
>>>a = [1,2,[3,4]]
>>>b = copy.deepcopy(a)
>>>id(a) #12177080
>>>id(b) #12177000
>>>a[2].append(5) #这时候 a = [1,2,[3,4,5]],而 b = [1,2,[3,4]]
>>>id(a[2][1]) #10379996
>>>id(b[2][1]) #10379996 这两个还是一样的,大惑不解,这是为什么?求达人解惑
经过linvo同学的指点,现在明白了为什么是这样。下面画个图:
如果只是浅拷贝(copy.copy),那么虚线部分是不会赋值给b的,只是把 [3,4]这个列表整体赋值。所以结果如代码3所示
而如果是深拷贝(copy.deepcopy),那么会把列表[3,4]的具体成员也赋给b,这也解释了代码4的疑惑
未完待续。。。。。。。。。。。。。。。。。