深浅拷贝

先来see see the code:

import copy
a = [1, 2, 3, 4, ['a', 'b']]  #原始对象
b = a  #赋值,传对象的引用
c = copy.copy(a)  #对象拷贝,浅拷贝
d = copy.deepcopy(a)  #对象拷贝,深拷贝
a.append(5)  #修改对象a
a[4].append('c')  #修改对象a中的['a', 'b']数组对象

print('a = ', a)
print('b = ', b)
print('c = ', c)
print('d = ', d)

#输出结果:
a =  [1, 2, 3, 4, ['a', 'b', 'c'], 5]
b =  [1, 2, 3, 4, ['a', 'b', 'c'], 5]
c =  [1, 2, 3, 4, ['a', 'b', 'c']]
d =  [1, 2, 3, 4, ['a', 'b']] 

赋值操作

1.对象的赋值就是简单的对象引用,他们指向同一片内存,b 不过是 a 的别名,是引用,它们的id是一样的

a = [1, 2, 3, 4, ['a', 'b']]  #原始对象
# 引用一
b = a  #赋值,传对象的引用
print(id(a),[id(x) for x in a])
print(id(b),[id(x) for x in b])
a[0] = 3
a[4].append("c")
print(id(a),id(b))
print(a,b)
print('..........')
#引用二
A =[1,2,3]
print(id(A),[id(x)for x in A])
# A[1] = A
# print(A)  # [1, [...], 3]
print(id(A),[id(x)for x in A])
# 167940360 [1910464736, 1910464768, 1910464800]
# 167940360 [1910464736, 1910464768, 1910464800]
# 只是A的结构发生了变化!但是,A还是指向那个对象地址
# 我们可以通过打印A的id来看,他的指向是没有变的!!

浅拷贝

1.浅拷贝会创建新对象,其内容非原对象本身的引用,而是原对象内第一层对象的引用。
可以归纳为,复制一份引用,新的对象和原来的对象的引用被区分开,但是内部元素的地址引用还是相同的.
2,不可变类型的对象,对于深浅拷贝毫无影响,最终的地址值和值都是相等的。
不可变对象不能修改只能替换,所以修改后自然就开了新的空间。
3,可变类型:
赋值浅拷贝: 值相等,地址相等
copy浅拷贝:值相等,地址不相等
deepcopy深拷贝:值相等,地址不相等
在有嵌套和可变类型的情况下,不使用深拷贝的拷贝和赋值都可以认为是浅拷贝

A =[1,2,3]
B = A[:]
print(A,B)
print(id(A),[id(x) for x in A])
print(id(B),[id(x) for x in B])
# 167940360 [1910464736, 1910464768, 1910464800]
# 167940296 [1910464736, 1910464768, 1910464800]
# 浅复制之后,A和B引用的内存地址是不同的
# 可以归纳为,复制一份引用,新的对象和原来的对象的引用被区分开,但是内部元素的地址引用还是相同的

A = [1,[1,2,3],3]
print(id(A),[id(x)for x in A])
B = A[:]
print(id(B),[id(x)for x in B])
#  不管是多么复杂的数据结构,浅拷贝只会拷贝第一层.

print(A is B)  # False
print(A[1] is B[1])  # True
A[0] = 8  # A和B两者是独立的引用
A[1][1] = 9  # A和B还共同引用着,对于嵌套的元素A和B还是指向同一块内存地址的。
print(A,B)
print(id(A),[id(x)for x in A])
print(id(B),[id(x)for x in B])
# 167744072 [1906663872, 167744136, 1906663712]
# 167744200 [1906663648, 167744136, 1906663712]
# 另外,对于不可变类型,修改之后,他的引用地址就变了!就原先这个元素的引用区分开了。
常见的浅拷贝的三种形式:切片操作、工厂函数、copy 模块中的 copy 函数
#切片操作
a = [1,2,3,[4,5]]
b = a[:]
b3 = 7
print(a) # [1,2,3,[7,5]]
print(b) # [1,2,3,[7,5]]
#工厂函数(list/dict/set)
a = (1,2,3,[4,5])
b = list(a)
b3 = 7
print(a) # (1,2,3,[7,5])
print(b) # [1,2,3,[7,5]]
#copy.copy()
import copy
a = (1,2,3,[4,5])
b = copy.copy(a)
b3 = 7
print(a) # (1,2,3,[7,5])
print(b) # [1,2,3,[7,5]]

深拷贝

深拷贝拷贝了对象的所有元素,包括多层嵌套的元素,
会复制原变量的所有数据,在另一块地址中创建一个新的变量或容器,同时变量内的子元素的地址也会重新独立开辟,仅仅是值相同而已,是完全的副本。在内存中生成一套完全一样的内容,我们对这两个变量中的一个进行任意修改都不会影响另一个变量。

两种 copy 只在面对复杂对象时有区别,所谓复杂对象,是指对象中含其他对象(如复杂的 list 和 class)。
由 shallow copy 建立的新复杂对象中,每个子对象,都只是指向自己在原来本体中对应的子对象。而 deep copy 建立的复杂对象中,存储的则是本体中子对象的 copy,并且会层层如此 copy 到底。


参考链接
参考链接

d={'L':[-1,0],'R':[1,0],'U':[0,1],'D':[0,-1]}
e = copy.copy(d)
f = copy.deepcopy(d)
e.pop('R')
print("d:",d)
print("e:",e)
print("f:",f)
e['L'].remove(0)
print("d:",d)
print("e:",e)
print("f:",f)
#输出结果
d: {'L': [-1, 0], 'R': [1, 0], 'U': [0, 1], 'D': [0, -1]}
e: {'L': [-1, 0], 'U': [0, 1], 'D': [0, -1]}
f: {'L': [-1, 0], 'R': [1, 0], 'U': [0, 1], 'D': [0, -1]}
d: {'L': [-1], 'R': [1, 0], 'U': [0, 1], 'D': [0, -1]}
e: {'L': [-1], 'U': [0, 1], 'D': [0, -1]}
f: {'L': [-1, 0], 'R': [1, 0], 'U': [0, 1], 'D': [0, -1]}
# 既然e指向与d同样的内存区域,那么e.pop('R')后,d为什么不改变呢?
# 因为e.pop('R')只是移除了'R'的引用,并没有在内存中删除'R':[1,0],而d没有进行任何操作,所以,d没有变。
# 而e['L'].remove(-1),是对e中'L'对应的子元素[-1,0]操作,移除了对0的引用(同样的,其实0依然在内存中存在),而d中的'L'和a中'L'指向同一个子元素,所以也d也跟着改变了。

你可能感兴趣的:(深浅拷贝)