Python之可变对象和不可变对象
-
可变对象(mutable):对象本身的值可以改变,即改变对象内部的值的 时候,这个对象的id不会发生变化
-
不可变对象(immutable):对象本身的值不能被修改,即改变对象内部的值的 时候,这个对象的id会发生变化
本章主要探讨(不)可变对象值一样id的变化
测试环境为3.9
- 可变对象
可变对象包括:list(列表)、dict(字典)、set(集合)
>>> #以list为例子
>>> a_list=[1,2,3]
>>> id(a_list) #输出:2961286586688
>>> a_list.append(4)
>>> #对可变数据类型的操作不能是直接进行新的赋值操作,比如说a=[1,2,3,4,5,6,7],这样的操作就不是改变值了,而是新建了一个新的对象,这里的可变只是对于类似于append、+=等这种操作。
>>> id(a_list) #输出:2961286586688
>>> #以list为例子
>>> a_list=[1,2,3]
>>> b_list=[1,2,3]
>>> id(a_list),id(b_list) #输出:2339722453440 2339719495616
- 不可变对象
不可变对象包括:int(整型)、float(浮点型)、str(字符串)、bool(布尔)、complex(复数)、truple(元组)
>>> a=1
>>> id(a) #输出:2350887299376
>>> a=2
>>> id(a) #输出:2350887299408
>>> #并不改变原始对象的值,只是创建一个新对象,并指向该变量,原始对象让Python的GC回收
- int型
>>> #int值比较小时,值一样的变量id就一样,值变化id就变化。
>>> a=1
>>> b=1
>>> a is b #输出:True
>>> #int值比较大时,值一样的变量id也不一样,值变化id就变化。
>>> a=257
>>> b=257
>>> a is b #输出:False
该情况是在cmd运行,较小的整数会很频繁的被使用,Python为了优化速度,使用了小整数对象池,避免为整数频繁申请和销毁内存空间。
Python对小整数的定义是 [-5, 256] 这些整数对象是提前建立好的,不会被垃圾回收。
在一个 Python 的程序中,无论这个整数处于LEGB中的哪个位置,所有位于这个范围内的整数使用的都是同一个对象。
当在Python的IDLE运行下,情况会有所不同。因为它使用的是大整数对象池。
在终端中,每次执行一次,所以每次大整数都重新创建。而在IDLE中,每次运行是所有代码都加载都内存中,属于一个整体,所以这个时候会有一个大整数对象池,即处于一个代码块的大整数是同一个对象。
- str型
>>> a="hello"
>>> b="hello"
>>> a is b #输出:True
>>> a="hello world"
>>> b="hello world"
>>> a is b #输出:False
>>>> a="12"
>>> b="12"
>>> a is b #输出:True
>>> a="1 2"
>>> b="1 2"
>>> a is b #输出:False
对于str型,Python采用intern机制。单词类型的str由于被重复使用的概率比较大,所以在python中为单词类型的str做了一个缓存。
也就是说如果是单词类型的str,会被存储到一个字典(dict)中,字典的内容是字符串为key,地址为value。当有一个字符串需要创建,就先去访问这个字典,如果存在则返回字典中字符串的地址,如果不存在,则返回新创建的地址,并将这个字符串添加进入字典。
intern机制处理空格一个单词的复用机会大,所以创建一次,有空格创建多次,但是字符串长度大于20,就不是创建一次了。
需要强调的是终端与IDLE的处理情况仍不相同,有兴趣的可自行实验https://www.jb51.net/article/183220.htm这篇帖子中还探讨了一个有趣的现象,与Python编译机制有关,欢迎大家拜读
- float型
>>> a=1.1
>>> b=1.1
>>> a is b #输出:False
对于float类型的使用自然没有int那么频繁,并且float类型也不好定义哪些常用,也就没有池子给到这个类型,所以每次重新创建即可
truple类型也与float类型相似
参考:https://www.cnblogs.com/shiyublog/p/10809953.html
https://www.jianshu.com/p/b3157c9751d0