python 不可变量和可变量(稍微深入)

来源:互联网 发布:苹果手机游戏优化器 编辑:程序博客网 时间:2024/05/18 01:39

@ 摘要:
Python的数据类型涉及到2个大原则 即 可变和不可变,是否可变显然说的变量代表的内存空间里的值。
本文简要概述python3的 (python2版本好像更奇葩) 并且和Java的相关内容进行类比。
以源码为依据,从时间和空间出发猜测为什么会这么做。
@ 作者: http://blog.csdn.net/vincent_ceso
@ 声明:不保证正确:) 转载无需注明 就说是你写的。

>>作者:http://blog.csdn.net/vincent_ceso


Python的不可变量

>字符

>数值

>元组

[不可变量的描述]

引用->对象:

  • 对于不可变量如果需要创建的对象的内容(value值)相同,则引用都指向同一个对象,不创建新的内存空间。
  • 理论上因为定义了值是不可变的。所以如果大家都一样的值,那就指向同一份内存空间好了。显然这么节省内存,避免冗余。
  • 但是实现的时候显然考虑到记录和维护这些变量。那么每次赋值的时候还涉及到一个查找是不是已经存在,引起了时间和空间的矛盾。实际上python实现的时候可是因地制宜的,会有很多的细节琐碎处。
#[demo1 数值(int float)]a = 10  #数值 但是是int 10b = 10  #数值 但是是int 10c = 10. #数值 但是是float 10.print(a is b)  #True 说明内存是相同的空间 这就是不可变导致的a和b数值相同,那么a,b指向同一个内存空间。print(a == b)  #True 数值相等print(a is c)  #False  显然数值不同,那么指向内存空间不同print(a == c)  #True 因为==是运算,即int==float 的不同类型元素涉及类型转换。#所以Python先把int变成float即再比较。显然是相同的a = 11  #修改值 值不同 那么就是新的内存空间了print(a is b) #False  显然数值不同,那么指向的内存空间不同'''琐碎处:python3 似乎没发现如下问题。python2 :  如果你的测试数值大于[-5,256]就发现出错。是版本问题。不同版本处理int的实现方式不同。  [小整数对象使用对象池技术]:Python直接将这些小范围的整数对应的PyIntObject缓存在内存中,其指针放在small_ints中。预先存储好了一堆小数值。  因为python是有一个smallint数组来维护的。相当于解释器的一个缓存,对于smallint,即[-5,256]范围,所以如果你的值在这个范围里,那就直接指向预先设好的这个内存空间。如果超出那么就是开辟新空间存储。  为什么?  因为维护这么一个smallint数组必须是高效的。首先因为维护和预先设定内存存储数据那就是内存的开销,而到时候new一个int对象还要涉及到查找是不是已存在于smallint里的CPU开销。  如果你经常使用1 2 3 这三个 那么显然就针对这三个维护起来很高效啊。但是实际中数值是很难预测的,即很难命中已缓存的。所以维护太多是毫无意义的。而且多了到时候你就要用个数字a++一下。结果人家先找到这个数字。找了10ms这么慢再返回。那岂不是还不如直接开辟的好。  时间和空间的协调。所以维护了一个小范围常用的的池子即可。'''
#[demo2 字符串]a = "cesto"b = "cesto"c = "XJ"print(a is b)  #True 说明指向的内存是相同的空间 这就是不可变print(a == b)  #True 数值相等print(a is c)  #False  显然数值不同,那么指向的内存空间不同print(a == c)  #False  值不同a = "XJ"print(a is b)  #False  值不同 指向的内存空间不同print(a is c)  #True 说明指向的内存是相同的空间 #说明没有新生成a 而是指向c指向的那个内存空间哦!!!'''【琐碎处】:如果测试数值相等,但是输出啊is b是False,还是版本问题。  因为字符串一般都是通过字典维护的,new一个记录下,下次如果是相同的那就直接指向相同内存空间不产生新的。  python3:实现了intern共享。  为啥?  因为字符串参与运算很少吧,存储的确很多。即字符串是一个存储为主导的。必然涉及到大量内存。所以节省内存是字符串高效的地方啊,所以我们舍弃时间追求空间。  python2里:没看过源码。似乎是有人说字符串很长很长的时候就无效。那么大概可以理解因为字符串那么太长涉及到的就是存储的压缩。导致维护的开销恒大,所以太长也就放弃了记录了。 '''
#[demo3 元组]a = (1,"cesto",[1])  #一个新的元组(1,"cesto")被a所指向b = (1,"cesto",[1])  #一个新的元组(1,"cesto")被b所指向print(a is b) #Flase 为什么?因为其实此时此刻产生的是俩个对象。没有实现intern机制print(a == b) #True 数值相等''' 即此时此刻是两个元素a和b,虽然他们的值相同,但是是俩个对象。而且这俩a和b是不可变的:即你修改他们只能是重新赋值,其实是指向了新的内存空间,而不能对原来指向的内存空间做出修改。原来的内存空间是还在的,然后很快就被GC掉。本质就是没实现Intern''''''【琐碎处】:  元组本身有很多不一样。就是因为元组的元素可以是任意的元素。即元素本身可以是不可变或者可变的。所以比较特殊。元组的不变就是说,凡是修改一个元组对象,必然是返回一个新的数组对象。而不可能是在原地址进行修改。  理论上应该和数值 字符串一样维护啊,但是没有?  为啥?  因为元组本身就没实现intern机制。其实本质元组是等价于C里的数组的。每次你new数组就是新创建。根本就没有维护和记录啊。  为啥不维护?  因为查找太慢啊。维护是为了避免冗余,但是相同的元组似乎出现的场景太少了吧那。  空间意义和时间意义都不大。那就不实现。'''#【看到一个以其昏昏,使人昭昭,强加附会的例子】a = (1)  #这是不是一个元组?b = (1)  print(a is b)   # True 为什么?print(type(a))  # 因为(1)此时可不是元组 而是int'''认为上述例子可以反驳 元组的实现情况 '''a = (1,)  #这才是一个元组b = (1,)  print(a is b)   # False 为什么?print(type(a))  # 因为(1,)此时是元组 

[小结]

  • 1.即如果不可变量的值相同,那么数值相同理论上完全指向同一个内存空间
  • 2.所以如果有指针修改了大家都指向的这一块内存空间 那么引用他的全部变量的值就一次性都变了。
    好在不支持指针,Python没有指针是避免了这个问题。
  • 3.这样的不可变变量就是: 数值 字符串 元组
  • 4.数值 字符串 基本都是按照“值相等那就是指向同一个内存空间” 避免冗余。内存浪费。
    因为实现了Intern机制。但是要注意是一定范围内才有效。
  • 5.其中元组比较特殊。只是保证了不可原地修改。但相等的元组不是指向同一个内存地址。
    因为本质tuple没有实现intern机制

Python的可变量

>列表

>字典

>集合

[可变量的描述]

引用->对象:

  • 对于可变量只要创建对象那就是本质是new一个新的内存空间,但是好处是能够修改。即修改对象值不会新开辟对象而是就是在原来内存空间改。
a=[1,2,3]  b=[1,2,3]  print(a is b ) #false  每次new新的,才不维护呢print(a==b) #true  '''可变量和引用是相对应的,即使对象的内容相等,也是不同的对象,修改的时候,两个对象互不影响'''print(id(a))  #2810505330120a.append(4)  # 修改值print(id(a)) #2810505330120 内存空间前后没变