深拷贝-浅拷贝

时间:2021-08-19 19:47:40

深拷贝浅拷贝是个很容易迷糊的问题,本人帮你彻底搞清楚。

 

粗识内存

本人没学过c,内存略懂,有堆栈之分,

栈可以理解为程序自动分配的内存,堆可以理解为程序员对内存的引用,不重要,有感觉就行。

 

浅拷贝

浅拷贝并不是我们认知中的“复制”,浅拷贝只是对象的引用,是对一个对象的浅层拷贝,所以叫浅拷贝

或者说是顶层拷贝,只拷贝了引用,没拷贝内容

 

示例代码

import copy

a = [1, 2, [3, 4]]
b = copy.copy(a)

print('b:', b)      # ('b:', [1, 2, [3, 4]])

b.append(5)
b[2].append(6)

print('a:', a)      # ('a:', [1, 2, [3, 4, 6]])
print('b:', b)      # ('b:', [1, 2, [3, 4, 6], 5])

为什么这样呢?

深拷贝-浅拷贝

b是a的浅拷贝,对b的操作不会影响a,但是对b内元素操作会影响a,因为ab指向的是同一个元素

b只拷贝了a的第一层,所以叫浅拷贝

print(id(a))        # 40641880
print(id(b))        # 40667616      b新建了一个对象
print(id(a[2]))     # 40643080
print(id(b[2]))     # 40643080      b中元素仍是a中元素

 

深拷贝

深拷贝才是我们认知中的“复制”,深拷贝是完全拷贝,是对一个对象的深层拷贝,所以叫深拷贝。

 

示例代码

import copy

a = [1, 2, [3, 4]]
b = copy.deepcopy(a)

print('b:', b)      # ('b:', [1, 2, [3, 4]])

b.append(5)
b[2].append(6)

print('a:', a)      # ('a:', [1, 2, [3, 4]])
print('b:', b)      # ('b:', [1, 2, [3, 4, 6], 5])

好理解了

深拷贝-浅拷贝

互不影响

b拷贝了a的所有层,所以叫深拷贝

print(id(a))        # 41624920
print(id(b))        # 41648496      b新建了一个对象
print(id(a[2]))     # 41626120
print(id(b[2]))     # 41648456      b中元素不是a中元素

 

可以看到深拷贝和浅拷贝都新建了一个对象。

 

等于

那等于是什么?深拷贝?浅拷贝?都不是,等于才是真正的复制。

 

示例代码

a = [1, 2, [3, 4]]
b = a

print('b:', b)      # ('b:', [1, 2, [3, 4]])

b.append(5)
b[2].append(6)

print('a:', a)      # ('a:', [1, 2, [3, 4, 6], 5])
print('b:', b)      # ('b:', [1, 2, [3, 4, 6], 5])


print(id(a))        # 40510648
print(id(b))        # 40510648      b没有新建对象
print(id(a[2]))     # 40490568
print(id(b[2]))     # 40490568      b中元素仍是a中元素

互相影响,因为是同一个对象

深拷贝-浅拷贝

 

 所以  复制 != 拷贝

 

好了,终于明白了,真的吗?

 

看个实例

jack = ['jack', ['age', 20]]
tom = jack[:]
anny = list(jack)
print id(jack), id(tom), id(anny)    # 144846988 144977164 144977388

3个不同的对象

tom[0] = 'tom'
anny[0] = 'anny'
print jack, tom, anny   # ['jack', ['age', 20]] ['tom', ['age', 20]] ['anny', ['age', 20]]

可以理解

anny[1][1] = 18
print jack, tom, anny   # ['jack', ['age', 18]] ['tom', ['age', 18]] ['anny', ['age', 18]]

请问,还理解吗?

 

分析:上面是新建了3个对象,那么只能是深拷贝或浅拷贝中的一种,改了名字互不影响,应该是深拷贝,但是改了年龄互相影响,又不是深拷贝,那是浅拷贝?也不对啊,迷糊了

[id(x) for x in jack]  # [3073896320L, 3073777580L]
[id(x) for x in tom]   # [144870744, 3073777580L]
[id(x) for x in anny]  # [144977344, 3073777580L]

可以看到名字id不同,年龄id相同

 

为什么会这样呢?

因为python中字符串是不可变类型,其被修改时,必须新建一个对象,这就明白了,就是浅拷贝,年龄指向了同一个id,但是因为名字修改时重新创建了对象, 所以指向了不同的id

 

那我们在上面浅拷贝的例子上验证下

b[2] = 3
print('a:', a)      # ('a:', [1, 2, [3, 4, 6]])
print('b:', b)      # ('b:', [1, 2, 3, 5])

果然改变了b[2],新建了个对象,a[2]并没有改变

 

如果用深拷贝就没有上面的问题

jack = ['jack', ['age', '20']]
import copy
tom = copy.deepcopy(jack)
anny = copy.deepcopy(jack)
# 根据第一个思路进行重命名,重定岁数操作:
tom[0] = 'tom'
anny[0] = 'anny'
print jack, tom, anny  # ['jack', ['age', '20']] ['tom', ['age', '20']] ['anny', ['age', '20']]
anny[1][1] = 18
print jack, tom, anny  # ['jack', ['age', '20']] ['tom', ['age', '20']] ['anny', ['age', 18]]

 

总结

切片操作和工厂方法list方法拷贝是浅拷贝,而且一般情况下都是浅拷贝

深拷贝浅拷贝需要考虑数据是否是可变类型