详解Python变量在内存中的存储

详解Python变量在内存中的存储,第1张

概述这篇文章主要是对python中的数据进行认识,对于很多初学者来讲,其实数据的认识是最重要的,也是最容易出错的。本文结合数据与内存形态讲解python中的数据,内容包括:引用与对象可变数据类型与不可变数据类型引用传递与值传递深拷贝与浅拷贝(id函数:你可以通过python的内置函数id()

这篇文章主要是对python中的数据进行认识,对于很多初学者来讲,其实数据的认识是最重要的,也是最容易出错的。本文结合数据与内存形态讲解python中的数据,内容包括:

引用与对象可变数据类型与不可变数据类型引用传递与值传递深拷贝与浅拷贝

(ID函数:你可以通过python的内置函数 ID() 来查看对象的身份(IDentity),这个所谓的身份其实就是 对象 的内存地址)

一、引用与对象:引用与对象的关系: 

 #创建两个对象 name1='wupeiqi' name2='alex'

对象:当创建数据对象时,在内存中会保存对象的值,这个值就是对象自己;(字符串对象:”wupeiqi”) 
引用:对象保存在内存空间,外部想要使用对象的值,需要使用引用,就是‘name1’,’name2’。内存会保存对象的引用数量,当某个对象的引用数量为0时,对象会被回收。

二、可变数据类型与不可变数据类型 
1,数据分类:

可变数据类型:列表List和字典dict不可变数据类型:整型int、浮点型float、字符串型string和元组tuple

这里的可变不可变,是指内存中的那块内容(value)是否可以被改变。如果是不可变类型,在对对象本身 *** 作的时候,必须在内存中新申请一块区域(因为老区域不可变)。如果是可变类型,对对象 *** 作的时候,不需要再在其他地方申请内存,只需要在此对象后面连续申请(+/-)即可,也就是它的address会保持不变,但区域会变长或者变短。

(1)python中的不可变数据类型,不允许变量的值发生变化,如果改变了变量的值,相当于是新建了一个对象,而对于相同的值的对象,在内存中则只有一个对象,内部会有一个引用计数来记录有多少个变量引用这个对象; 
(2)可变数据类型,允许变量的值发生变化,即如果对变量进行append、+=等这种 *** 作后,只是改变了变量的值,而不会新建一个对象,变量引用的对象的地址也不会变化,不过对于相同的值的不同对象,在内存中则会存在不同的对象,即每个对象都有自己的地址,相当于内存中对于同值的对象保存了多份,这里不存在引用计数,是实实在在的对象。”

2,不可变数据类型:不可变是指对象本身的值是不可变的(当你创建a=1整型对象,用a去引用它,内存中的对象1是不变得,当执行a=2时,只是重新创建了对象2,用a引用,如果1对象没有其他引用会被回收)

 >>> x = 1 >>> ID(x) 31106520 >>> y = 1 >>> ID(y) 31106520 >>> x = 2 >>> ID(x) 31106508 >>> y = 2 >>> ID(y) 31106508 >>> z = y >>> ID(z) 31106508

解释:这里的不可变大家可以理解为x引用的地址处的值是不能被改变的,也就是31106520地址处的值在没被垃圾回收之前一直都是1,不能改变,如果要把x赋值为2,那么只能将x引用的地址从31106520变为31106508,相当于x = 2这个赋值又创建了一个对象,即2这个对象,然后x、y、z都引用了这个对象,所以int这个数据类型是不可变的,如果想对int类型的变量再次赋值,在内存中相当于又创建了一个新的对象,而不再是之前的对象。从下图中就可以看到上面程序的过程。 

3,可变对象:可变是指对象本身的值是可变的(List,dict对象的值其实是引用了其他对象,当改变对象的值时,其实是引用了不同的对象)

 >>> a = [1, 2, 3] >>> ID(a) 41568816 >>> a = [1, 2, 3] >>> ID(a) 41575088 >>> a.append(4) >>> ID(a) 41575088 >>> a += [2] >>> ID(a) 41575088 >>> a [1, 2, 3, 4, 2]

解释:(1)进行两次a = [1, 2, 3] *** 作,两次a引用的地址值是不同的,也就是说其实创建了两个不同的对象,这一点明显不同于不可变数据类型,所以对于可变数据类型来说,具有同样值的对象是不同的对象,即在内存中保存了多个同样值的对象,地址值不同。 
(2)我们对列表进行添加 *** 作,分别a.append(4)和a += [2],发现这两个 *** 作使得a引用的对象值变成了上面的最终结果,但是a引用的地址依旧是41575088,也就是说对a进行的 *** 作不会改变a引用的地址值,只是在地址后面又扩充了新的地址,改变了地址里面存放的值,所以可变数据类型的意思就是说对一个变量进行 *** 作时,其值是可变的,值的变化并不会引起新建对象,即地址是不会变的,只是地址中的内容变化了或者地址得到了扩充。下图对这一过程进行了图示,可以很清晰地看到这一过程。 

三、引用传递与值传递:可变对象为引用传递,不可变对象为值传递。(函数传值) 
1,引用传递:当传递列表或者字典时,如果改变引用的值,就修改了原始的对象。

 # 添加了一个string类型的元素添加到末尾   def changelist(lis): lis.append('hello i am the addone') print lis return   lis = [1, 2, 3] changelist(lis) print lis   输出: [1,2,3, 'hello i am the addone']   [1,2, 3,'hello i am the addone']

2,值传递:当传递不可变对象时,如果改变引用的值,只是创建了不同的对象,原始对象并没有改变。

 def ChangeString(string): string = 'i changed as this' print string return   string = 'hello world' ChangeString(string) print string   输出: i changed as this   hello world

四、深拷贝与浅拷贝: 
copy.copy() 浅拷贝;copy.deepcopy() 深拷贝。浅拷贝是新创建了一个跟原对象一样的类型,但是其内容是对原对象元素的引用。这个拷贝的对象本身是新的,但内容不是。拷贝序列类型对象(列表\元组)时,默认是浅拷贝。

1,赋值拷贝: 
赋值,只是创建一个变量,该变量指向原来内存地址:n4 = n3 = n2 = n1 = “123/’Wu’” 

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

 import copy n1 = {"k1": "wu", "k2": 123, "k3": ["alex", 456]} n3 = copy.copy(n1)

 import copy a = [1,[[2,3],5],3] b = a.copy() #copy.copy(a)   print(ID(a[1])) print(ID(b[1]))   c = copy.deepcopy(a) print(ID(c[1]))   输出: 3021497843400 3021497843400 3021497854728

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

 import copy n1 = {"k1": "wu", "k2": 123, "k3": ["alex", 456]} n4 = copy.deepcopy(n1)

参考文献: 
http://blog.csdn.net/dan15188387481/article/details/49864613 
https://www.cnblogs.com/lfpython/p/7207747.html 
https://www.cnblogs.com/huamingao/p/5809936.html 
https://www.cnblogs.com/jiangzhaowei/p/5740913.html

转自:https://www.cnblogs.com/wangshicheng/p/10627916.html

总结

以上是内存溢出为你收集整理的详解Python变量在内存中的存储全部内容,希望文章能够帮你解决详解Python变量在内存中的存储所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/1186318.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-03
下一篇 2022-06-03

发表评论

登录后才能评论

评论列表(0条)

保存