赋值、浅拷贝、深拷贝
python的深浅拷贝之前我学习过,直到今天我才发现我对深浅拷贝、赋值有一个误区。所以我想写篇文章来纠正一下自己,也提醒新学的人不要犯我的错误。
1.赋值
我之前以为,赋值是这样的。给大家画个图
a = [1, 2, 3]
b = a
print(a, b)
直到今天我发现原来赋值是这样的
print(id(a), id(b))
解释:两个id一样,那是不是说明赋值并没有创建一个新的对象,a,b公用一个对象,即如下图所示。
2.浅拷贝
把赋值的真正含义弄懂了,那浅拷贝就容易多了。浅拷贝:只拷贝父对象,不会拷贝对象的内部的子对象。这么说会有点抽象,那么举个例子来说明,就容易理解多了。
import copy
a = [1,2,3]
b = copy.copy(a)
print(id(a),id(b))
print(a,b)
结果:
2811085694088 2811085694280
[1, 2, 3] [1, 2, 3]
解释:
我们可以看到两个id是不相同的,说明是两个不同的对象
如果是那样的话,再举一个例子。来验证验证我的想法。
import copy
a = [1,2,[3]]
b = copy.copy(a)
a.append(4)
print(a,b)
输出结果:
我想的 :[1, 2, [3], 4] [1, 2, [3], 4]
实际的:[1, 2, [3], 4] [1, 2, [3]]
我有点懵逼,是我哪里搞错了?难道不应该是我想的那样?
浅拷贝难道不是拷贝的是原来列表的地址么?后来通过看书我才明白了,浅拷贝真正拷贝的是容器元素的地址。那我们再举个例子看一下。
import copy
a = [1,2,[3]]
b = copy.copy(a)
for x in a:
print(id(x))
for y in b:
print(id(y))
结果输出:
1431797872 1431797904 1774225499272
1431797872 1431797904 1774225499272 (是拷贝的元素id没错吧)
当我们给a新添加一个元素时,a就产生了一个新的元素id,在当这之前b已经拷贝走了,所以它不知道。
当我们给一个已经存在的元素里再添加一个元素,就比如[1,2,[3]]的[3]添加一个元素时,它的id会不会变?答案是当然不会变,那么在b中就能看到这个元素。 下面我们举个例子吧
i
import copy
a = [1,2,[3]]
b = copy.copy(a)
a.append(4)
print(a,b)
输出结果:
[1, 2, [3], 4] [1, 2, [3]] (因为新添加的元素产生了一个新的id)
在这里考大家一个小题目:
b = a[:] 这个是浅拷贝呢 还是赋值呢?
答案:浅拷贝,因为他不仅仅是赋值了引用值,还把a里的元素(也就是id)传了进去。
3.深拷贝
深拷贝就很接近我们生活中理解的那个复制、粘贴一样,它拷贝了一个副本。就是b把a里的元素全部复制了一遍,自己新弄了一个。
import copy
a = [1,2,[3]]
b = copy.deepcopy(a)
for x in a:
print(id(x))
for y in b:
print(id(y))
输出结果:
1431797872 1431797904 1701157119112
1431797872 1431797904 1701157112486
大家发现了应该是三个id不一样啊,但是为什么前两个一样。我就想着如果我修改a中第二个元素的值,那b会不会改变?如果是完全拷贝的话,那肯定不会改变。
import copy
a = [1,2,[3]]
b = copy.deepcopy(a)
a[1]=111
print(a,b)
结果输出: [1, 111, [3]] [1, 2, [3]] (没有改变是拓过来的一个样本)
之所以会出现id一样的情况,我认为是为了系统之间为了节省内存,一样的东西不会占用两个内存。
4.跳坑
1.大家知道元组里的元素是不可遍历的。如果想深拷贝元组里的元素,是不行的。
2.对于数字、字符串这种元素,它是不能拷贝的。
5.总结
1. 赋值是将一个对象的地址赋值给了另一个一个变量,这两个变量都指向该地址。
2. 浅拷贝是创建了一个新的变量,但是里面的元素是源对象的元素的地址。
3. 深拷贝是创建一个新的变量,里面的元素的地址也是新的,仅仅是值相同而已。(相当于生活中的复制粘贴)