python深浅copy探究
引入
在python程序中,如果我们操作一个变量的值去做运算,而又想在下次调用时,仍使用原来的变量的值去做运算,那么我们我们就需要将这个变量去做备份,这就是本文所要探究的问题。
开始
变量-对象-引用:
python中全部皆对象,Python中变量是指对象(甚至连type其本身都是对象,type对象)的引用,Python是动态类型,程序运行时候,会根据对象的类型来确认变量到底是什么类型。
我们有时候会见到这样一种情况:
a = 1
b = a
这样做不就是把数据copy了一份吗,错,这样做只是在程序中新创建了一个对象b,去引对象a创建是指定的值,也就是说,它们是指向了同一块内存地址,而不是在创建 b = a 时又重新在内存中开辟出一块空间来,所以当一个变量的值被修改,则另一个变量的值也会随之被修改,这样说可能不太理解,来个图示吧:
这个叫做"共享引用",而不是做一个副本(不要犯这样的错哦)。
正题
现在就开始探究一下深浅copy
深浅copy的方法由python内置的一标准库提供(提供的copy方法,适合所有类型对象)
简单点说
- copy.copy 浅拷贝 只拷贝父对象,不会拷贝对象的内部的子对象。
- copy.deepcopy 深拷贝 拷贝对象及其子对象
用一个简单的例子来说明一下
>>> import copy >>> a = [1,2,3,['a','b','c']] >>> b = a >>> c = copy.copy(a) >>> d = copy.deepcopy(a)
a是一个列表,表中的a[3]也是一个列表(也就是一个内部的子对象),b是对a列表的又一个引用,所有a,b是完全相同的,我们可以通过id(a)来验证。
>>> id(a) 60356208 >>> id(b) 60356208 >>> id(c) 63132896 >>> id(d) 63133256
a 和 b的相同也证实了前面所说的共享引用;
深浅copy的区别:
浅copy
>>> import copy >>> a = [1,2,3,['a','b','c']] #a具有两级对象的列表 >>> b = copy.copy(a) #创建变量b,使其浅copy a的值 >>> b [1, 2, 3, ['a', 'b', 'c']] >>> a[1] = "two" #当a的父对象修改之后而b的父对象不会被修改 >>> a [1, 'two', 3, ['a', 'b', 'c']] >>> b [1, 2, 3, ['a', 'b', 'c']] >>> a[3][0] = 'A' #当a的子对象被修改之后b的子对象也会随之修改 >>> a [1, 'two', 3, ['A', 'b', 'c']] >>> b [1, 2, 3, ['A', 'b', 'c']]
深copy
>>> import copy >>> a = [1,2,3,['a','b','c']] #a具有两级对象的列表 >>> b = copy.deepcopy(a) #创建变量b,使其深copy a的值 >>> b [1, 2, 3, ['a', 'b', 'c']] >>> a[1] = "two" #当a的父对象修改之后而b的父对象不会被修改 >>> a [1, 'two', 3, ['a', 'b', 'c']] >>> b [1, 2, 3, ['a', 'b', 'c']] >>> a[3][0] = 'A' #当a的子对象被修改之后b的子对象仍不会随之修改 >>> a [1, 'two', 3, ['A', 'b', 'c']] >>> b [1, 2, 3, ['a', 'b', 'c']]
浅拷贝是指拷贝的只是原对象元素的引用,换句话说,浅拷贝产生的对象本身是新的,但是它的内容不是新的,只是对原对象的一个引用
>>> aList=[[1, 2], 3, 4] >>> bList = aList[:] #利用切片完成一次浅拷贝 >>> id(aList) 3084416588L >>> id(bList) 3084418156L >>> aList[0][0] = 5 >>> aList [[5, 2], 3, 4] >>> bList [[5, 2], 3, 4]
但是有点需要特别提醒的,如果对象本身是不可变的,那么浅拷贝时也会产生两个值
>>> aList = [1, 2] >>> bList = aList[:] >>> bList [1, 2] >>> aList [1, 2] >>> aList[1]=111 >>> aList [1, 111] >>> bList [1, 2]
为什么bList的第二个元素没有变成111呢?因为数字在python中是不可变类型!!
在这顺便回顾下Python标准类型的分类:
- 可变类型: 列表,字典
- 不可变类型:数字,字符串,元组
理解了浅拷贝,深拷贝是什么自然就很清楚了。
python中有一个模块copy,deepcopy函数用于深拷贝,copy函数用于浅拷贝。
最后,对象的赋值是深拷贝还是浅拷贝?
对象赋值实际上是简单的对象引用
>>> a = 1 >>> id(a) 135720760 >>> b = a >>> id(b) 135720760
a和b完全是一回事。