python 深浅拷贝

一、深浅拷贝的区别

对于数字和字符串这种不可变对象(不包括tuple),深浅拷贝没有什么意义,如文章所说,只有对组合对象而言有不同,如列表、类实例等。

拷贝分为浅拷贝copy和深拷贝deepcopy。当然,浅拷贝不止copy一种,包括直接复制、切片等等也是。

1.对于数字和字符串而言,没有什么区别,因为他们指向同一个内存地址。
2.而对于字典、元祖、列表等可变量(元组属于不可变量,但它有些特殊,详细的可以参考我这篇)而言,进行赋值、浅拷贝和深拷贝时,其内存地址的变化是不同的。

1)赋值,只是创建一个变量,该变量指向原来内存地址。

>>> a = [1,2]
>>> b = a

a和b的地址完全相同

2)浅拷贝,在内存中只额外创建第一层数据。

>>> from copy import copy
>>> l1 = [1,2]
>>> l2 = l1
>>> l3 = copy(l1)
>>> id(l1), id(l2), id(l3)
(2069879756744, 2069879756744, 2069879213832)

>>> for i,j,k in zip(l1,l2,l3):
       print(id(i), id(j), id(k))
    
1409346000 1409346000 1409346000
1409346032 1409346032 1409346032

可以看到,l1、l2指向的内存地址相同,但它们和l3指向的内存地址不同,但这三个列表其内部包含的元素如1,2指向的地址相同。具体的结果是怎样,可以看第二部分的验证。

3)深拷贝,在内存中将所有的数据重新创建一份(排除最后一层,即:python内部对字符串和数字的优化)

>>> from copy import deepcopy
>>> l1 = [1,2]
>>> l2 = deepcopy(l1)
>>> id(l1), id(l2)
(2069879756744, 2069879960968)

>>> for i,j in (l1,l4):
       print(id(i), id(j))
    
1409346000 1409346032
1409346000 1409346032

目前看来,和浅拷贝的结果类似。l1和l2指向的内存地址不同,其内部元素地址仍然相同。但这是因为其内部元素皆为不可变对象,比如示例代码中的1,2,对应的内存地址仍是相同的,但若是可变对象也不同,请继续看下面的示例。

二、多种复制对象的id(可变对象中包含可变对象)

接下来我们看几个复制的例子:

>>> from copy import copy,deepcopy
>>> a = [1,2]
>>> b = a
>>> c = list(a)
>>> d = copy(a)
>>> e = deepcopy(a)
>>> f = a[:]

>>> id(a),id(b),id(c),id(d),id(e),id(f)
(47481096L, 47481096L, 47429256L, 47431432L, 47784840L, 47475144L)

>>> for i,j,k,m,n,p in zip(a,b,c,d,e,f):
	    print id(i),id(j),id(k),id(m),id(n),id(p)
	
30080104 30080104 30080104 30080104 30080104 30080104
30080080 30080080 30080080 30080080 30080080 30080080

这里用了5中拷贝的方法,分别是,直接赋值、list、copy、deepcopy、切片。
可以看到,直接赋值和原列表的id是一样的,其他的都各不相同,但是他们遍历的元素的id却全部相同。

可以看到所有不可变元素id相同,因为python会给你返回一个从缓存中找到的某个类型和值都一致的对象的引用。而如果列表里包含了可变对象,那就不一样了。

>>> h = [3,4,[1,2]]
>>> i = deepcopy(h)
>>> id(h),id(i)
(47506504L, 47516104L)

>>> for m,n in zip(h,i):
	print id(m),id(n)
	
30080056 30080056
30080032 30080032
47507848 47514248

可以看到,那两个整数对象的id仍然相等,但是最后一个列表元素的id就不一样了。当然了,这个列表的列表里的元素的id仍然相等。

>>> for m,n in zip(h[2],i[2]):
	print id(m),id(n)

30080104 30080104
30080080 30080080

三、被拷贝对象发生更改之后

>>> a
[1, 2]
>>> a.append(3)
>>> a,b,c,d,e,f
([1, 2, 3], [1, 2, 3], [1, 2], [1, 2], [1, 2], [1, 2])
>>> id(a),id(b),id(c),id(d),id(e),id(f)
(47481096L, 47481096L, 47429256L, 47431432L, 47784840L, 47475144L)
# 之前(47481096L, 47481096L, 47429256L, 47431432L, 47784840L, 47475144L)

可以看到仍旧只有直接赋值的b与原对象a的value同时发生了更改,其他全都没有改变。id也完全没有更改,通过append等方式改变列表,id是不会改变的,除非你重新赋值增加元素,id才会改变。

>>> a = [1,2,3,4]
>>> b,c,d,e,f
([1, 2, 3], [1, 2], [1, 2], [1, 2], [1, 2])

>>> id(a),id(b),id(c),id(d),id(e),id(f)
(47507336L, 47481096L, 47429256L, 47431432L, 47784840L, 47475144L)

如果通过append的方式,id不会发生任何改变。而重新给a赋值,只有a的id改变,其他的id和value仍然没有改变。

四、列表中的列表

>>> from copy import copy,deepcopy
>>> a = ['a',[1,2],[3,4]]
>>> a1 = a
>>> a2 = copy(a)
>>> a3 = deepcopy(a)

>>> a[0]='b'
>>> a[1].append(3)
>>> a[2] = [4,5]

# 在以上这部分做完改变后,再分别输出a,a1,a2,a3会是什么?

>>> a
['b', [1, 2, 3], [4, 5]]
>>> a1
['b', [1, 2, 3], [4, 5]]
>>> a2
['a', [1, 2, 3], [3, 4]]
>>> a3
['a', [1, 2], [3, 4]]

如前面所说,copy和deepcopy都是拷贝,并且拷贝之后与原对象没有关系。

对于deepcopy,原对象是可变量或不可变量,是增删改查都无所谓,它不会做任何更改,它完全是一份新的对象,子元素的id与原先的也完全不同。

而copy,如果是不可变量,则不会发生改变,但若是碰上了可变量,因为它列表里的元素的id和原先的指向的是同一个位置。所以原来的增加了,也会增加。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值