首页 > 技术文章 > python中的深浅copy

yuan-x 2020-01-10 17:58 原文

1 对于python来说,一切变量都是对象,python中变量的存储,采用了引用语义,存储的只是一个变量的值所在的内存地址,而不是这个变量的只本身。这样来存储的空间大小一致,因为变量只是保存了一个内存地址。

c语言使用的是值语义,把变量的值直接储存在变量的储存区中,这样就造成一个变量占用的内存可大可小

数据类型重新初始化和数据结构内部元素变化对python语义引用的影响

   a 数据类型初始化:    

str1='cap'
print(id(str1))
str1='nihao'
print(id(str1))
打印结果 

   47647576
   48673936

在2次初始化str1的时候,str1指向的内存地址发生了改变

  b 数据结构内部元素变化:

    

lst1=[1,2,3,4,5,6]
print(id(lst1))
打印结果34682952

lst1.append('yuan')
print(id(lst1))
打印结果34682952

lst1.pop()
print((lst1))
print(id(lst1))
打印结果34682952

lst1[0]='test'
print(lst1)
print(id(lst1))
打印结果34682952

#重新给lst1赋值
lst1=[1,2,3,4]
print(id(lst1))
打印结果40539656

在上述的list的增删改中 lst1的内存地址并没有改变,但是在重新赋值的时候发生了变化
因此在python中数据结构是可以包含基础数据的。每个变量只是储存了值的引用(内存地址)
而每个数据结构内的元素也是只是储存了元素的内存地址


3 拷贝(浅拷贝和深拷贝)

  对于复杂的数据结构来说,赋值就等于完全共享了资源,一个值的改变会完全被另一个值共享。

  然而有的时候,我们偏偏需要将一份数据的原始内容保留一份,再去处理数据,这个时候使用赋值就不够明智了。

   python为这种需求提供了copy模块。提供了两种主要的copy方法,一种是普通的copy,另一种是deepcopy。我们称前者是浅拷贝,后者为深拷贝。

   copy是指在内存中完全是2块完全独立的内存空间,id值不一样 如果2个变量应用的是同一个对象地址,则能称之为copy

   浅拷贝:

      只拷贝一层,即将被拷贝对象中的内存地址引用过来

      

import copy
lsta=['y6','y7','y8','y9','y10']
sourcelst=['y1','y2','y3','y4','y5',lsta]
copy_lst=copy.copy(sourcelst)
print(sourcelst)
print(copy_lst)

结果:
  ['y1', 'y2', 'y3', 'y4', 'y5', ['y6', 'y7', 'y8', 'y9', 'y10']]
  ['y1', 'y2', 'y3', 'y4', 'y5', ['y6', 'y7', 'y8', 'y9', 'y10']]

sourcelst.append('y20')
copy_lst.append('y21')
print(sourcelst)
print(copy_lst)

结果:
['y1', 'y2', 'y3', 'y4', 'y5', ['y6', 'y7', 'y8', 'y9', 'y10'], 'y20']
['y1', 'y2', 'y3', 'y4', 'y5', ['y6', 'y7', 'y8', 'y9', 'y10'], 'y21']

sourcelst[0]='yx'
print(sourcelst)
print(copy_lst)

结果
['yx', 'y2', 'y3', 'y4', 'y5', ['y6', 'y7', 'y8', 'y9', 'y10'], 'y20']
['y1', 'y2', 'y3', 'y4', 'y5', ['y6', 'y7', 'y8', 'y9', 'y10'], 'y21']

lsta.append('test')
print(sourcelst)
print(copy_lst)

结果
['yx', 'y2', 'y3', 'y4', 'y5', ['y6', 'y7', 'y8', 'y9', 'y10', 'test'], 'y20']
['y1', 'y2', 'y3', 'y4', 'y5', ['y6', 'y7', 'y8', 'y9', 'y10', 'test'], 'y21']

sourcelst和copy_list只是储存的是变量的地址,所以当soucelst改变索引0的值的时候只是将'y1'的内存地址替换成'yx'的,但是copy_lst的索引0的值还是y1的内存地址

但是当修改了lsta的时候,souucels和copy_lst都回发生改变,因为它们两个存的都是lsta的内存地址

在copy复杂的数据类型的时候,要特别注意

深拷贝:
深拷贝,在内存中将所有的数据重新创建一份,并且拷贝到最后一层。这样两份数据就不会完全影响
lsta=['y6','y7','y8','y9','y10']
sourcelst=['y1','y2','y3','y4','y5',lsta]
copy_lst=copy.deepcopy(sourcelst)
print(sourcelst,id(sourcelst))
print(copy_lst,id(copy_lst))

结果
['y1', 'y2', 'y3', 'y4', 'y5', ['y6', 'y7', 'y8', 'y9', 'y10']] 41988680
['y1', 'y2', 'y3', 'y4', 'y5', ['y6', 'y7', 'y8', 'y9', 'y10']] 41989960

 

sourcelst.append('source')
copy_lst.append('copy')
print(sourcelst,id(sourcelst))
print(copy_lst,id(copy_lst))

结果
['y1', 'y2', 'y3', 'y4', 'y5', ['y6', 'y7', 'y8', 'y9', 'y10'], 'source'] 41988680
['y1', 'y2', 'y3', 'y4', 'y5', ['y6', 'y7', 'y8', 'y9', 'y10'], 'copy'] 41989960
lsta.append('lsta')
print(sourcelst,id(sourcelst))
print(copy_lst,id(copy_lst))


结果
['y1', 'y2', 'y3', 'y4', 'y5', ['y6', 'y7', 'y8', 'y9', 'y10', 'lsta'], 'source'] 41988680
['y1', 'y2', 'y3', 'y4', 'y5', ['y6', 'y7', 'y8', 'y9', 'y10'], 'copy'] 41989960

 

无论是直接改变source和copy的列表内元素,还是改变引用的可变数据类型来看,都不会对copy的数据造成影响
深拷贝在开辟内存空间的时候,遇到可变数据类型,就会在将可变数据类型中的数据内存地址全部复制下来,直到最后一层,保持原引用,所以原数据修改不会对copy的数据造成影响



 

 

 

推荐阅读