深浅copy

铺垫(深入理解内存地址)

学习深浅copy之前,我们先来看一个例子

1 l1 = [1, 2, 3]
2 l2 = l1
3 l2.append(4)
4 print(l1, l2)

猜猜结果是怎样的?l1是打印[1,2,3]还是[1,2,3,4],来看结果

[1, 2, 3, 4] [1, 2, 3, 4]

看到没,l2添加了一个元素4后l1也跟着改变,这说明l1,l2指向的是同一个内存地址,我们可以用is验证一下

print(l1 is l2)

执行结果

True

说明l1和l2的指向同一个内存地址,l1,l2的内存地址相同,那么它们的元素的内存地址是否相同呢,来看

print(id(l1[0]), id(l2[0]))

执行结果

1445227552 1445227552

元素的内存地址也相同,列表里的元素不仅仅是数字,也可以是列表或者字典等可变数据类型,那么对于这些可变数据类型,它们的内存地址是否相同呢,来看代码

1 l1 = [1, 2, [3, 4, 5]]
2 l2 = l1
3 print(id(l1), id(l2))
4 print(id(l1[1]), id(l2[1]))
5 print(id(l1[-1]), id(l2[-1]))

执行结果

1950802883592 1950802883592
1445227584 1445227584
1950802883656 1950802883656

可见对于可变数据类型,内存地址也是相同的。因此我们可以得出结论,对于赋值运算,是将原对象的内存地址赋值给新对象,等号两边的对象的内存地址和元素的内存地址都是完全相同的。

 

浅copy

浅copy是利用列表或字典的copy方法来复制,其内部机制是怎样的呢,来看一段代码

 1 l1 = [1, "abc", [1, 2]]
 2 l2 = l1.copy()
 3 print(id(l1), id(l2))   # 内存地址不同
 4 print(id(l1[0]), id(l2[0]))
 5 print(id(l1[-1]), id(l2[-1]))    # 内存地址相同
 6 l1.append(3)
 7 print("l1   ", l1)
 8 print("l2   ", l2)     # l2没有加上3
 9 l1[-2].append(3)
10 print("l1   ", l1)
11 print("l1   ", l2)

执行结果

1156212831240 1156212831432
1445227552 1445227552
1156212831304 1156212831304
l1 [1, 'abc', [1, 2], 3]
l2 [1, 'abc', [1, 2]]
l1 [1, 'abc', [1, 2, 3], 3]
l1 [1, 'abc', [1, 2, 3]]

再来看看字典

 1 dic1 = {1: "a", 2: "b", 3: [1, 2, 3]}
 2 dic2 = dic1.copy()
 3 print(id(dic1), id(dic2))
 4 print(id(dic1[1]), id(dic2[1]))
 5 print(id(dic1[3]), id(dic2[3]))
 6 dic1[4] = "m"
 7 print(dic1)
 8 print(dic2)
 9 dic1[3].append(4)
10 print(dic1)
11 print(dic2)

执行结果

2260199788616 2260199788688
2260199617232 2260199617232
2260229796936 2260229796936
{1: 'a', 2: 'b', 3: [1, 2, 3], 4: 'm'}
{1: 'a', 2: 'b', 3: [1, 2, 3]}
{1: 'a', 2: 'b', 3: [1, 2, 3, 4], 4: 'm'}
{1: 'a', 2: 'b', 3: [1, 2, 3, 4]}

 

可以看出,浅copy的机制是:

1. 新开辟一个内存地址(新的容器)

2. 把原列表(或字典)的对应元素的内存地址放入新列表(字典)里面

也就是说创建了一个新的容器,但是里面的内容跟原容器是一样的,原容器的原内容(不包括新添加的)发生改变,新容器的对应内容也会发生改变。

 

浅copy不仅可以通过copy方法,还可以通过列表的切片

1 l1 = [1, "abc", [1, 2]]
2 l2 = l1[:]
3 print(id(l1), id(l2))    # id不一样

执行结果

2183077803016 2183077803208

因此列表的切片(全切)是浅copy

深copy

深copy可以利用copy模块里的deepcopy方法

1 import copy
2 l1 = [1, "abc", [1, 2]]
3 l2 = copy.deepcopy(l1)
4 print(id(l1), id(l2))
5 print(id(l1[0]), id(l2[0]))      # 内存地址相同(不可变数据类型)
6 print(id(l1[1]), id(l2[1]))      # 内存地址相同
7 print(id(l1[-1]), id(l2[-1]))    # 内存地址不同(可变数据类型)

执行结果

2267524916744 2267524918024
1445227552 1445227552
2267522383976 2267522383976
2267524916552 2267524917960

由此我们可以总结深copy的原理:

1. 新开辟一个内存地址(新的容器)

2. 对于不可变元素,沿用之前的(内存地址相同)

3. 对于可变元素,重新创建一份(内存地址不同)

 

posted @ 2018-12-30 17:21  乘月归  阅读(178)  评论(0编辑  收藏  举报