python的可变类型与不可变类型

  首先,我们需要知道在python中哪些是可变数据类型,哪些是不可变数据类型。可变数据类型:列表list和字典dict;不可变数据类型:整型int、浮点型float、字符串型string和元组tuple。

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

  不可变类型以int为例:

>>> x=1
>>> id(x)
1431399904
>>> y=2
>>> id(y)
1431399936
>>> z=3
>>> id(z)
1431399968
>>> x=x+1
>>> id(x)
1431399936
>>> x+=1
>>> id(x)
1431399968

  这里值得一提的是当x的值变化时,x引用的数据的内存地址发生了变化,换而言之,x引用了其他的数据,而不是在原来的内存地址上将1的引用计数置为0,然后回收内存地址改为1。python的不可变数据类型会开辟一块新的地址空间存储一个新的数据,然后将变量名指向新的地址。在我们这里的test中又不得不提到一个python的小数池概念。

>>> m=1234
>>> n=1235
>>> id(m)
2197878893424
>>> id(n)
2197878893392
>>> m=m+1
>>> id(m)
2197878893296
>>> o=n
>>> id(o)
2197878893392

  当x值加1以后x与y等值,此时x与y的值均指向同一个内存地址,那么x并没有开辟新的内存空间,这与我们之前说的python的不可变数据类型的方式是不相同的,其实python中为了减少开辟内存造成的时间开销,对于三位内的整型数字类型数据,在开辟一块内存空间后,后面的三位内的整型数据都放到这个内存空间中,所以三位以内的整型数字类型的id值都是相同的。

  引用计数

  此时m的值由1234变为1235,原先的1234的引用计数变为一,等待python的垃圾清理机制回收内存地址。此时的内存中m与n的值相等,但是他们的内存指向的是不同的地址空间,此时各自的数据引用计数为1,将新变量o也指向n,那么此时n对应的数据的引用计数+1。

  可变数据类型以列表为例:

>>> x=[1,2,3]
>>> y=[1,2,3,4]
>>> id(x)
1665579614408
>>> x.append(4)
>>> x
[1, 2, 3, 4]
>>> id(x)
1665579614408
>>> x=x+[5]
>>> x
[1, 2, 3, 4, 5]
>>> id(x)
1665579617992
>>> x+=[6]
>>> x
[1, 2, 3, 4, 5, 6]
>>> id(x)
1665579617992
>>> x.extend([7])
>>> x
[1, 2, 3, 4, 5, 6, 7]
>>> id(x)
1665579617992
>>> id(y)
1665579615240

  可以看出,只有x=x+n的时候x的内存地址发生了变化,此时为将两个列表组合成一个新的列表对象。列表本身是在原来的内存地址上修改值。

原文地址:https://www.cnblogs.com/Jeffding/p/8547917.html