初识Python内存机制
-
a = '123' print(id(a)) # 1492379299488
-
is 是 判断两边内存地址是否相等,如果内存地址相等,那么两个其实指向同一个内存地址
-
== 是 比较两边数值是否相等
>>> a = 100 >>> b = 100 >>> a is b True >>> a == b True >>> a = 1000 >>> b = 1000 >>> a is b False >>> a == b True
可以说内存地址相同,那么值肯定相同,但是值相同,内存地址不一定相同
代码块
Python程序是由代码块构造的。块是一个python程序的文本,他是作为一个单元执行的。
-
代码块:一个模块,一个函数,一个类,一个文件等都是一个代码块。
-
而作为交互方式输入的每个命令都是一个代码块。
def func1(): pass # 代码块 def func2(): pass # 代码块
代码块的缓存机制
-
Python在执行同一个代码块的初始化对象的命令时,会检查是否其值是否已经存在,如果存在,会将其重用
-
执行同一个代码块时,遇到初始化对象的命令时,他会将初始化的这个变量与值存储在一个字典中,在遇到新的变量时,会先在字典中查询记录,如果有同样的记录那么它会重复使用这个字典中的之前的这个值。所以在你给出的例子中,文件执行时(同一个代码块)会把i1、i2两个变量指向同一个对象,满足缓存机制则他们在内存中只存在一个,即:id相同
-
适用对象:int (float) str bool
-
int(float):任何数字在同一个代码块下都会复用
-
bool:True和False在字典中会以0,1方式存在,并且复用
-
str:几乎所有的字符串都符合缓存机制,具体如下
-
非乘法得到的字符串都满足代码块的缓存机制,只含有大小写字母,数字,下划线时
s1 = "holle" s2 = "holle" s1 is s2 True
-
乘法得到的字符串分两种情况
-
乘数为1时,任何字符串满足代码块的缓存机制
-
乘数>=2时:仅含大小写字母,数字,下划线,总长度<=20,满足代码块的缓存机制
-
-
-
-
-
能够提高一些字符串,整数处理任务在时间和空间上的性能,需要相同字符串,整数的时候,直接从‘字典’取出来复用,避免频繁的创建和销毁,提升效率,节约内存
小数据池
小数据池,不同代码块的缓存机制,也称为小整数缓存机制,或者称为驻留机制等等
Python自动将-5~256的整数进行了缓存,当你将这些整数赋值给变量时,并不会重新创建对象,而是使用已经创建好的缓存对象。
python会将一定规则的字符串在字符串驻留池中,创建一份,当你将这些字符串赋值给变量时,并不会重新创建对象, 而是使用在字符串驻留池中创建好的对象。
其实,无论是缓存还是字符串驻留池,都是python做的一个优化,就是将~5-256的整数,和一定规则的字符串,放在一个‘池’(容器,或者字典)中,无论程序中那些变量指向这些范围内的整数或者字符串,那么他直接在这个‘池’中引用,言外之意,就是内存中之创建一个。
-
适用对象:int(float),str,bool
-
int:对于整数来说,小数据池的范围是-5~256,如果多个变量指向同一个范围内的数字,他们指向同一个内存地址
a = 100 b = 100 c = 100 print(id(a),id(b),id(c)) # 1639937888 1639937888 1639937888
-
str:字符串要从下面几个大方向分析
-
字符串长度为0或者1,默认采用了驻留机制(小数据池)
a = '@' b = '@' a is b True
-
字符串的长度>1,且子只含有大小写字母,数字,下划线时,才会默认驻留
a = "nayue_jintian_meiqian" b = "nayue_jintian_meiqian" a is b True
-
用乘法得到的字符串,分两种情况
-
乘数为1时
-
仅包含大小写字母,数字,下划线,默认驻留
a = "nayue_jintian_meiqian" b = a * 1 a is b True
-
含其他字符,长度 <= 1,默认驻留
-
含其他字符,长度 > 1,默认驻留
-
-
乘数>=2时
仅包含大小写字母,数字,下划线,总长度 <= 20 ,默认驻留
a = 'na_y'*4 b = 'na_y'*4 a is b True
-
-
指定驻留
from sys import intern a = intern('hello!@'*20) b = intern('hello!@'*20) print(a is b) #指定驻留是你可以指定任意的字符串加入到小数据池中,让其只在内存中创建一个对象,多个变量都是指向这一个字符串。
-
-
bool值就是True,False,五六你创建多少个变量指向True,False,它在内存中只存在一个
-
深浅copy就是完全复制一份,和部分复制一份
-
赋值运算
ls1 = ['na','yue',['cat','xin']] ls2 = ls1 ls1[0] = 'dd' ls1 # ['dd', 'yue', ['cat', 'xin']] ls2 # ['dd', 'yue', ['cat', 'xin']] ls1[2][1] = 'dog' ls1 # ['dd', 'yue', ['cat', 'dog']] ls2 # ['dd', 'yue', ['cat', 'dog']]
对于赋值运算来说,ls1与ls2指向的是同一个内存地址,所以他们是完全一样的,任何一个变量对列表进行改变,剩下那个变量在使用列表之后,这个列表是发生了改变之后的列表
-
浅拷贝copy
# 同一代码块下 ls1 = [1,'那月',True,(1,2,3),[22,33]] ls2 = ls1.copy() print(id(ls1),id(ls2)) # 2685384972616 2685384972360 print(id(ls1[-2]),id(ls2[-2])) # 2685384946744 2685384946744 print(id(ls1[-1]),id(ls2[-1])) # 2685384956360 2685384956360 print(id(ls1[-1][0]),id(ls2[-1][0])) # 1630301600 1630301600 # 不同代码块下 >>> ls1 = [1,'那月',True,(1,2,3),[22,33]] >>> ls2 = ls1.copy() >>> print(id(ls1),id(ls2)) # 2092396391432 2092396391496 >>> print(id(ls1[-2]),id(ls2[-2])) # 2092396269432 2092396269432 >>> print(id(ls1[-1]),id(ls2[-1])) # 2092396390472 2092396390472 >>> print(id(ls1[-1][0]),id(ls2[-1][0])) # 1630301600 1630301600
-
对于浅copy来说,只是在内存中创建了一个空间存放一个新列表,但新列表中的元素与愿列表中的元素是公用的

-
-
深拷贝deepcopy
# 同一代码块下 import copy ls1 = [1,'那月',True,(1,2,3),[22,33]] ls2 = copy.deepcopy(ls1) print(id(ls1),id(ls2)) # 2087560861704 2087560882952 print(id(ls1[-2]),id(ls2[-2])) # 2087560836152 2087560836152 print(id(ls1[-1]),id(ls2[-1])) # 2087560861768 2087560881928 print(id(ls1[-1][0]),id(ls2[-1][0])) # 1630301600 1630301600 # 不同代码块下 >>> import copy >>> ls1 = [1,'那月',True,(1,2,3),[22,33]] >>> ls2 = copy.deepcopy(ls1) >>> print(id(ls1),id(ls2)) # 2092396391560 2092396391432 >>> print(id(ls1[-2]),id(ls2[-2])) # 2092396306504 2092396306504 >>> print(id(ls1[-1]),id(ls2[-1])) # 2092396395976 2092396403528 >>> print(id(ls1[-1][0]),id(ls2[-1][0])) # 1630301600 1630301600
- 对于deepcopy来说,列表是在内存中重新创建的,列表中可变的数据类型是重新创建的,列表中不可变数据类型是公用的

思考:
ls1 = [1, 2, ['cat']] ls2 = ls1[::] ls1.append('yue') print(ls2)

浙公网安备 33010602011771号