一 python的变量与以及存储
变量是对内存的以及地址的抽象,对于python而言python的一切变量都是对象,变量的存储采用了引用语义的方式,存储的只是一个变量所在值的内存地址,而不是这个变量的本身 。
引用语义:在python中,变量的保存时对象(值)的应用,称之为引用语义,采用这种语义,变量所需存储的空间大小一致。因为变量只是一个保存的引用。
值: 把变量的值直接保存在变量的存储区域里,采用这种存储方式 ,每一个在内存中所占的空间,就要根据变量的实际大小来定,无法固定下来。
二 各个基本的数据结构地址存储以及改变情况。
在python中的数据类型包括:bool,int,long,float,str,set,list,tucp,dict 等等 ,
这些数据类型可以分为简单数据类型,和复杂数据类型。
简单数据类型和复杂数据类型划分依据:
如果一个数据类型可以将其他的数据类型作为自己的元素。 则可以认为是一种数据结构,数据结构的分类有很多种,在python中常用的只有集合,序列和映射这三种数据结构 。 对应的是 set, list(tuple,str),dict,常用的数据类型有 int , long,float,bool,str 等 。
由于python中的变量都是采用的引用语义,数据结构可以包含基础数据类型,导致了在python中的数据存储方式存在下图所示的这种情况,每个变量中都存储了这个变量的地址,而不是值本身;对于复杂的数据结构来说。里面的存储也只是每个元素的地址而已
当对列表进行增,删,改查工作的时候 ,是不会影响到对于list整个本身的地址 ,只会改变其内部元素的地址应用, 可是当对列表进行重新赋值的时候,就给lst1这个列表地址赋予了一个新的地址,覆盖了原本列表的地址,这个时候,lst1的内存id就发生了变化。
内存的变化 , 其实的赋值操作都让str1,str2都存储了'hello wolrd' 是这个地址 ,重新对str1 赋值,是str1中存储的地址发生了改变,重新只想了新建的地址此时str2变量的存储的内存地址并为改变,所以不受影响。
2 复杂的数据结构的赋值
上序代码做了修改操作,但是并没有对lst2做出改变,结果lst1和lst2都发生了变化,
四 拷贝简述
上述内容讲述了变量赋值的过程,对于复杂的数据结构来说,赋值就等于完全共享了资源,一个值的改变会完全被另外一个值给共享。
五 浅拷贝
不管多么复杂的数据结构浅拷贝只会copy一层
lst = ['str1', 'str2', 'str3', 'str4', 'str5']
sourceLst = ['str1', 'str2', 'str3', 'str4', 'str5', lst]
copyLst = copy.copy(sourceLst)
print('1.->sourceLst:', sourceLst)
print('1.->copyLst:', copyLst)
sourceLst.append('sourcestr')
copyLst.append('copystr')
print('2.->sourceLst:', sourceLst)
print('2.->copyLst:', copyLst)
sourceLst[0] = 'changestr'
print('3.->sourceLst:', sourceLst)
print('3.->copyLst:', copyLst)
lst.append('testAppend')
print('4.->sourceLst:', sourceLst)
print('4.->copyLst:', copyLst)
上述代码执行会是下述结果:
复制代码
1.->sourceLst: ['str1', 'str2', 'str3', 'str4', 'str5', ['str1', 'str2', 'str3', 'str4', 'str5']]
1.->copyLst: ['str1', 'str2', 'str3', 'str4', 'str5', ['str1', 'str2', 'str3', 'str4', 'str5']]
2.->sourceLst: ['str1', 'str2', 'str3', 'str4', 'str5', ['str1', 'str2', 'str3', 'str4', 'str5'], 'sourcestr']
2.->copyLst: ['str1', 'str2', 'str3', 'str4', 'str5', ['str1', 'str2', 'str3', 'str4', 'str5'], 'copystr']
3.->sourceLst: ['changestr', 'str2', 'str3', 'str4', 'str5', ['str1', 'str2', 'str3', 'str4', 'str5'], 'sourcestr']
3.->copyLst: ['str1', 'str2', 'str3', 'str4', 'str5', ['str1', 'str2', 'str3', 'str4', 'str5'], 'copystr']
4.->sourceLst: ['changestr', 'str2', 'str3', 'str4', 'str5', ['str1', 'str2', 'str3', 'str4', 'str5', 'testAppend'], 'sourcestr']
4.->copyLst: ['str1', 'str2', 'str3', 'str4', 'str5', ['str1', 'str2', 'str3', 'str4', 'str5', 'testAppend'], 'copystr']
上述代码中, sourceLst和copyLst中存储的都是地址,当独自修改各个list的时候,另一个不会改变。而当修改共有的lst元素的时候,sourceLst和copyLst都会发生改变,这种情况发生在字典套字典,列表套字典,字典套列表,列表套列表,以及各种复杂数据结构的嵌套中。
六、深拷贝
上述讲述了浅拷贝,而在实际情况中,我们希望复杂的数据结构之间完全copy,而他们之间又没有一毛钱关系。
为此,python引入了深拷贝的概念,我们可以使用copy模块中的deepcopy方法。深拷贝会完全复制原变量相关的所有数据,在内存中生成一套完全一样的内容,在这个过程中我们队这两个变量中的一个进行任意修改都不会影响其他变量。
浅拷贝的情况:
lst1 = [1,2,3,4,[5,6,7,8]]
lst2 = lst1 # 实际进行了一个浅拷贝
id(lst2)
4363600648
id(lst1)
4363600648
lst1[0] = 100
lst1
[100, 2, 3, 4, [5, 6, 7, 8]]
lst2
[100, 2, 3, 4, [5, 6, 7, 8]]
lst2[4][0] = 100
lst1
[100, 2, 3, 4, [100, 6, 7, 8]]
复制代码import copy
lst1 = [1,2,3,4,[5,6,7,8]]
lst2 = copy.deepcopy(lst1)
id(lst1)
4363594504
id(lst2)
4363593928
lst1[0] = 100
lst2
[1, 2, 3, 4, [5, 6, 7, 8]]
lst2[4][0]=100
lst1
[100, 2, 3, 4, [5, 6, 7, 8]]
复制代码
从上述代码中可以看出,进行深拷贝后,lst1和lst2的地址不一样,而且两个独自修改并不影响另一个的值,他俩现在一毛钱关系都没有。
深拷贝就是在内存中重新开辟一块空间,不管数据结构多么复杂,只要遇到可能发生改变的数据类型,就重新开辟一块内存空间把内容复制下来,知道最后一层,不再有复杂的数据类型,就保持其原引用,这样,不管数据结构多么复杂,数据之间的修改都不会相互影响。这就是深拷贝~