【python】 一文弄懂浅拷贝与深拷贝

一、可变对象与非可变对象

  想要理解浅拷贝与深拷贝就必须得先明白可变对象与非可变对象。

  可变对象有: list ,dict ,set

        不可变对象有:int ,float ,bool ,str ,tuple 。

  两者区别:对于可变对象,其值改变是在原地址上操作,不会创建新的内存地址。对于不可变对象其值改变是直接创建新的内存地址。

二、赋值操作符‘=’表示对象的引用。

  python里面的 赋值操作符‘=’实际上是对象的引用,其并没有进行复制操作。如果如果赋值操作符右边的操作数是字面值,比如字符串或者数字,那么左边的操作数被设为一个对象的引用,该对象将指向存放字面值得内存对象。(a=1,a指向1的内存对象)。如果右边的操作数是一个对象引用,那么左边的操作数将设置为一个对象引用,并与右边的操作数是一个对象引用,并与右边的操作数指向相同的对象。(b=a,则a,b均指向1的内存对象)

>>> a=1       #a引用对象1(指向1的地址)
>>> b=a       # b引用对象a实际上也是引用对象1  (b指向1的地址)
>>> id(a)     
94474006287136
>>> id(b)
94474006287136
>>> a=2           #a引用对象2,由于对象2相对对象1发生了改变,则重新创建新的内存里面                    存的是对象2 (a指向2处的地址)
>>> id(a)
94474006287168
>>> id(b)       # b继续引用之前的对象1 (b还是指向之前1的地址不变)
94474006287136
>>> 
>>> a=[1,2,3,4]
>>> b=a
>>> b
[1, 2, 3, 4]
>>> a.append(5)
>>> a
[1, 2, 3, 4, 5]
>>> b
[1, 2, 3, 4, 5]

由上可以发现列表a赋值给b时,实际上a和b都指向同一内存地址。由于列表a是可变对象,对a进行append操作后是在原地址上操作,所以b也跟着改变了。

二、浅拷贝 copy.copy()

  1.对于不可变类型 Number String Tuple,浅复制仅仅是地址指向,不会开辟新空间。

  

>>> a='hello world'
>>> b=a
>>> id(a)
140620341876400
>>> id(b)
140620341876400
>>> c=copy.copy(a)
>>> c
'hello world'
>>> id(c)
140620341876400
>>> 

对于下面的情况:将a的地址指向改变下,但是b和c的地址仍然不变,指向原来的地址。

>>> a=5   # 将a的地址指向由‘hello world'改为指向5
>>> id(a)
94474006287264
>>> id(b)
140620341876400
>>> id(c)
140620341876400
>>> 

  2.对于可变类型 List、Dictionary、Set,浅复制会开辟新的内存存放其指向的地址(仅仅是最顶层开辟了新的内存,里层的元素地址还是一样的) 

>>> a=[1,2,3,[4,5,6],7,8]   #列表中子对象(子列表[4,5,6])
>>> b=a
>>> c=copy.copy(a)
>>> id(a)        
140620341875976
>>> id(b)
140620341875976
>>> id(c)           # 浅copy 外层开辟新的内存,c的内存地址与a不一样
140620341876104
>>> id(a[3])         # 里层的内存地址
140620341814920
>>> id(d[3])
140620341875720
>>> id(c[3])         # 浅拷贝后的里层的内存地址与原对象的里层内存地址一样。里层在浅copy时                 作为公共部分使用,浅拷贝时这部分不会重新创建新的内存
140620341814920

  3. 可变类型(只有可变类型才能对应后面的改变)浅拷贝后,改变原始对象中为可变类型的元素的值,会同时影响拷贝对象的;改变原始对象中为不可变类型的元素的值,只有原始类型受影响。

    

>>> a=[1,2,3,{'a':'A'}]
>>> c=copy.copy(a)
>>> id(a)
140620341875976
>>> id(c)
140620341680264
>>> a[0]=0
>>> a
[0, 2, 3, {'a': 'A'}]
>>> c
[1, 2, 3, {'a': 'A'}]
>>> id(a[0])
94474006287104
>>> id(c[0])
94474006287136
>>> a[3]['a']='AAA'
>>> a
[0, 2, 3, {'a': 'AAA'}]
>>> c
[1, 2, 3, {'a': 'AAA'}]
>>> id(a[3])
140620366602104
>>> id(c[3])
140620366602104
>>> 

  总结:浅拷贝(复制)只是拷贝了对象引用,而非对象本身。对于不可变数据,浅拷贝就当于我们所理解的复制,此时也等价于深拷贝。对于可变数据类型,其元素是不可变时与上述一样,如果是可变元素时,比如列表中有嵌套列表,嵌套列表的引用也被复制,当嵌套列表改变时,其内存地址不变即浅拷贝后其嵌套列表也是跟着变得。

三、深拷贝copy.deepcopy()

        深拷贝就是通常我们所理解的,完全的拷贝并重新创建新的内存空间(包括外层和里层)。

>>> a=[1,2,3,[4,5,6]]
>>> b=copy.deepcopy(a)
>>> id(a)
140620341875976
>>> id(b)
140620341876104
>>> a[3][0]=0
>>> a
[1, 2, 3, [0, 5, 6]]
>>> b
[1, 2, 3, [4, 5, 6]]

四、其他等价的浅拷贝。

       除了浅拷贝copy.copy外,对于列表还是其他的操作等价于浅拷贝。比如切片和类型名作为函数。

使用类型名作为函数的还有如下

copy_of_dict=dict(d)

copt_of_set=set(s)

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值