一、代码块及其缓存机制
代码块
一个模块、一个函数、一个类、一个文件等都是一个代码块;交互式命令下,一行就是一个代码块。
同一个代码块内的缓存机制(字符串驻留机制)
机制内容:Python在执行同一个代码块的初始化对象的命令时,会检查是否其值是否已经存在,如果存在,会将其重用,即将两个变量指向同一个对象。换句话说:执行同一个代码块时,遇到初始化对象的命令时,他会将初始化的这个变量与值存储在一个字典中,在遇到新的变量时,会先在字典中查询记录,如果有同样的记录那么它会重复使用这个字典中的之前的这个值。所以在你给出的例子中,文件执行时(同一个代码块)会把i1、i2两个变量指向同一个对象,满足缓存机制则他们在内存中只存在一个,即:id相同。
适用对象: int(float),str,bool。
对象的具体细则:(了解)
int(float):任何数字在同一代码块下都会复用。
bool:True和False在字典中会以1,0方式存在,并且复用。
str:几乎所有的字符串都会符合字符串驻留机制
不同代码块间的缓存机制(小数据池、小整数缓存机制、小整数驻留机制)
适用对象: int(float),str,bool
具体细则:-5~256数字,bool,满足一定规则的字符串。
优点:提升性能,节省内存。
Python自动将-5~256的整数进行了缓存,当你将这些整数赋值给变量时,并不会重新创建对象,而是使用已经创建好的缓存对象。
python会将一定规则的字符串在字符串驻留池中,创建一份,当你将这些字符串赋值给变量时,并不会重新创建对象, 而是使用在字符串驻留池中创建好的对象。
其实,无论是缓存还是字符串驻留池,都是python做的一个优化,就是将~5-256的整数,和一定规则的字符串,放在一个‘池’(容器,或者字典)中,无论程序中那些变量指向这些范围内的整数或者字符串,那么他直接在这个‘池’中引用,言外之意,就是内存中之创建一个。
#同一个代码块内的缓存机制————任何数字在同一代码块下都会复用i1 = 1000i2 = 1000print(id(i1))print(id(i2))输出结果:3210011232100112
#同一个代码块内的缓存机制————几乎所有的字符串都会符合缓存机制s1 = 'hfdjka6757fdslslgaj@!#fkdjlsafjdskl;fjds中国's2 = 'hfdjka6757fdslslgaj@!#fkdjlsafjdskl;fjds中国'print(id(s1))print(id(s2))输出结果:3161667231616672
#同一个代码块内的缓存机制————非数字、str、bool类型数据,指向的内存地址一定不同t1 = (1,2,3)t2 = (1,2,3)l1 = [1,2,3]l2 = [1,2,3]print(id(t1))print(id(t2))print(id(l1))print(id(l2))输出结果:32191328321914723221069632232712
二、深浅copy
赋值运算讨论
令y=x,然后对x重新赋值时:
若x, y为不可变数据类型(可哈西的数据类型)(int, str, bool, tuple),则y不会跟着x的变化而变化
若x, y为可变数据类型(不可哈西的数据类型)(list, dict, set),则y会跟着x的变化而变化
#int的例子 x=1 y=x x=x+1 print(x) print(y) 输出结果: 2 1
#str的例子 x='abc' y=x x=x+'d' print(x) print(y) 输出结果: abcd abc
#bool的例子 x=True y=x x=False print(x) print(y) 输出结果: False True
#tuple的例子 x=(1,2) y=x x=(3,4) print(x) print(y) 输出结果: (3, 4) (1, 2)
#list的例子 x=[1,2] y=x x.append(3) print(x) print(y) 输出结果: [1, 2, 3] [1, 2, 3]
#dict的例子 x={1:2,3:4} y=x x[5]=6 print(x) print(y) 输出结果: {1: 2, 3: 4, 5: 6} {1: 2, 3: 4, 5: 6}
#set的例子 x={1,2} y=x x.add(3) print(x) print(y) 输出结果: {1, 2, 3} {1, 2, 3}
不可变数据类型(可哈西的数据类型)(int, str, bool, tuple)原理图:
可变数据类型(不可哈西的数据类型)(list, dict, set)原理图:
浅copy
拷贝目标可变对象(list, dict, set)时,仅为第一层可变对象分配新的内存地址,第二层及以上的可变对象沿用之前对象的内存地址,此外所有层的不可变对象(int, str, bool, tuple)均沿用之前对象的内存地址。
#更改第一层的例子 l1 = [1, 2, 3, [22, 33]] l2 = l1.copy() l1.append(666) print(l1,id(l1)) print(l2,id(l2)) print(l1[0],id(l1[0])) print(l2[0],id(l2[0])) print(l1[3],id(l1[3])) print(l2[3],id(l2[3])) 输出结果: [1, 2, 3, [22, 33], 666] 43242760 [1, 2, 3, [22, 33]] 43243208 1 1414365296 1 1414365296 [22, 33] 43220744 [22, 33] 43220744
#更改第二层的例子 l1 = [1, 2, 3, [22, 33]] l2 = l1.copy() l1[-1].append(666) print(l1, id(l1)) print(l2, id(l2)) print(l1[0],id(l1[0])) print(l1[0],id(l2[0])) print(l1[-1],id(l1[-1])) print(l1[-1],id(l2[-1])) 输出结果: [1, 2, 3, [22, 33, 666]] 43046152 [1, 2, 3, [22, 33, 666]] 43046600 1 1414365296 1 1414365296 [22, 33, 666] 43024136 [22, 33, 666] 43024136
浅copy的另一种表示方式(此种方式必须在前面写import copy搭建模块,否则报错!)
import copy x=[1,2,3] y=copy.copy(x)
深copy
拷贝目标可变对象(list, dict, set)时,为所有层的可变对象分配新的内存地址,此外所有层的不可变对象(int, str, bool, tuple)均沿用之前对象的内存地址。
必须在前面写import copy搭建模块,否则报错!
import copy l1 = [1, 2, 3, [22, 33]] l2 = copy.deepcopy(l1) print(l1,id(l1)) print(l2,id(l2)) print(l1[0],id(l1[0])) print(l1[0],id(l2[0])) print(l1[-1],id(l1[-1])) print(l1[-1],id(l2[-1])) 输出结果: [1, 2, 3, [22, 33]] 43650504 [1, 2, 3, [22, 33]] 43754248 1 1414365296 1 1414365296 [22, 33] 43650312 [22, 33] 43754184