1. 可变和不可变数据类型
1.1 概念
可变数据类型:当指定值被修改时内存空间地址不变
不可变数据类型:当指定值被修改时内存空间地址发生改变
1.2 常见类型的代码实现
可变类型:list dict set
不可变类型:str int float bool tuple
(1)整数 不可变
a = 1 print(id(a)) # 2549145403632 a = 2 print(id(a)) # 2549145403632
(2)浮点型 不可变
b = 1.01 print(id(b)) # 1369240625488 b = 2.02 print(id(b)) # 1369240630864
(3)字符串 不可变
c = 'messi' print(id(c)) # 2077972897840 c = 'leomessi' print(id(c)) # 2077972974448
(4)布尔值 不可变
d = True print(d, type(d), id(d)) # True <class 'bool'> 140709452925800 d = False print(d, type(d), id(d)) # False <class 'bool'> 140709452925832
(5)元组 不可变
e = (1, 2, 3) print(e, type(e), id(e)) # (1, 2, 3) <class 'tuple'> 2759659910528 e = e.__add__((9, 10, 11)) print(e, type(e), id(e)) # (1, 2, 3, 9, 10, 11) <class 'tuple'> 2759660046368
(6)列表 可变
f = [1, 2, 3] print(f, type(f), id(f)) # [1, 2, 3] <class 'list'> 2513866568320 f.append(666) print(f, type(f), id(f)) # [1, 2, 3, 666] <class 'list'> 2513866568320
(7)字典 可变
g = {'name': 'messi'} print(g, type(g), id(g)) # {'name': 'messi'} <class 'dict'> 2838915653376 g['name'] = 'Leomessi' print(g, type(g), id(g)) # {'name': 'Leomessi'} <class 'dict'> 2838915653376
(8)集合 可变
h = {'messi', 'ronaldo'} print(h, type(h), id(h)) # {'ronaldo', 'messi'} <class 'set'> 2297948368096 h.add(11) print(h, type(h), id(h)) # {11, 'ronaldo', 'messi'} <class 'set'> 2297948368096
总结:在python中可变与不可变的区别是值被修改后内存空间地址是否改变
1.3 值传递和引用传递
(1)概念
值传递:每一次的值都会有一个自己的内存空间地址
引用传递:在中间有一块内存地址负责中转对应的值
严格意义上来说,python既不是值传递,也不是引用传递
(2)python的传递规则是:
如果传递的是不可变类型,在函数中修改,就不会影响原来的变量
如果传递的是可变数据类型,在函数中修改,不会影响原来的变量,修改,而不是重新赋值
(3)值与引用的概念
值是一个变量 = 具体的值(一块内存空间放着这个变量的值)
引用是一个变量 = 内存地址(内存地址指向了值)
(4)参数传递
可变类型的参数传递:
如列表、字典,在函数中修改参数会影响原始对象
不可变类型的参数传递: 如数字、字符串,在函数中修改参数不会影响原始对象# 可变数据类型就是修改原来的值但是内存空间地址不变
# 不可变数据类型就是修改原来的值但是内存空间地址会改变
2. 堆和栈的概念
堆(是一种数据结构):先进先出
栈(是一种数据结构):先进后出
3. 垃圾回收机制
3.1 概念:
垃圾回收机制(简称GC)是Python解释器自带的一种机制,用来回收不可用的变量值所占用的内存空间(在内存中,没有变量名指向的数据都是垃圾数据)
3.2 作用:
程序运行过程中会申请大量的内存空间,而对于一些无用的内存空间如果不及时清理的话会导致内存使用殆尽(内存溢出),导致程序崩溃
3.3 堆区与栈区
在定义变量时,变量名与变量值都是需要存储的
栈区:变量名与值内存地址的关联关系通常被存放在程序的栈区
堆区:变量值存放于堆区,内存管理回收的是堆区的内容
3.4 原理
引用计数为主,分代回收、标记清除为辅
(1)引用计数
a = 1 # 变量值1的引用次数是1 b = a = 1 # 变量值1的引用次数是2 b = a = 2 # 变量值1的引用次数是0 a = 1 # 变量值1的引用次数是1
解释器会将引用次数为0的数据清除
(2)标记清除
内存在每次运行时都会进行扫描
第一次扫描---发现某个值变成了垃圾---不会立即清除---可能在某块代码又对该值进行了引用---对该垃圾打标签---出现一次
第二次扫描---发现该值仍然是垃圾---不会立即清除---对该垃圾打标签---出现两次
当标签次数为10次进行权重提升
(3)分代回收
每一次扫描都会发现垃圾 垃圾未必是当前必须清除掉的垃圾 打上标签
等级1 : 隔 10 min 扫描一次, 一直发现有一个垃圾存在 ,当标记次数达到 100 次就提权
等级2 : 隔 20 min 扫描一次 ,一直发现有一个垃圾存在 ,当标记次数达到 200 次的时候就提权
等级3 : 隔 30 min 扫描一次, 一直发现有一个垃圾存在, 当标记次数达到 300 次的时候就清除
垃圾可能会被捡起来重新使用 为了避免重复开销内存 于是就有了上述机制
4. 小整数池
python中经常使用的一些数值定义为小整数池,范围是[-5,256],python对这些数值已经提前创建好了内存空间
即使多次重新定义也不会在重新创建新的空间,但是小整数池外的数值在重新定义时都会再次创建新的空间。
所以对于小整数池中的数,内存地址一定是相同的,小整数池外的数,内存地址是不同的。
5. 深浅拷贝
5.1 深拷贝
是指创建一个新的对象,该对象与原始对象完全独立
递归地复制所有嵌套对象,包括内容,以便在修改新对象时不会影响到原始对象
(完整复制)
import copy a = [1, 2, 3, [7, 8, 9]] b = copy.deepcopy(a) # 深拷贝产生的列表 print(b) # [1, 2, 3, [7, 8, 9]] # 修改原列表中的可变类型中的元素 a[3][0] = 666 print(a) # 原列表[1, 2, 3, [666, 8, 9]] print(b) # 深拷贝列表[1, 2, 3, [7, 8, 9]] # 修改原列表中的不可变类型 a[1] = 222 print(a) # [1, 222, 3, [666, 8, 9]] print(b) # [1, 2, 3, [7, 8, 9]]
新对象和原对象对可变数据类型的引用不一致
5.2 浅拷贝
是指创建一个新的对象,并将原始对象的元素复制到新对象中
然而,如果原始对象包含可变的元素(如列表),则新对象中的这些元素仍然与原始对象中相应元素共享相同的内存地址
(只会复制一层)
import copy a = [1, 2, 3] b = copy.copy(a) # 浅拷贝产生新的列表 a.append(666) print(a) # 原来的列表 [1, 2, 3, 666] print(b) # 浅拷贝的列表 [1, 2, 3] b.append(999) print(b) # 修改浅拷贝的列表不会影响原列表 [1, 2, 3, 999]
import copy c = [1, 2, 3, [7, 8, 9]] d = copy.copy(c) # 浅拷贝产生的列表 print(d) # [1, 2, 3, [7, 8, 9]] # 修改原列表中的可变数据类型中的元素 c[3][0] = 666 print(c) # 原列表[1, 2, 3, [666, 8, 9]] print(d) # 浅拷贝的列表[1, 2, 3, [666, 8, 9]]
# 修改原列表中的不可变类型 c[1] = 222 print(c) # [1, 222, 3, [666, 8, 9]] print(d) # [1, 2, 3, [666, 8, 9]]
新对象和原对象中对可变数据类型的引用一致
总结:
深拷贝是创建一个完全独立的对象,并且递归的复制对象中的所有关系
浅拷贝是创建一个新对象,但是新对象共享原对象中的可变数据类型关联关系
6. 字符编码
6.1 概念:
计算机只能识别二进制数,人类与计算机交互时,用的都是人类能读懂的字符,如中文字符、英文字符
人类常用的字符与计算机中的数字交互,一定经历一个转换翻译过程,转换翻译的过程必须参照一个特定的标准,该标准称之为字符编码表
字符编码表上存放的是字符与数字的一 一对应关系
编码即指人类常用的字符转换成计算机能够识别的数字
6.2 字符编码的发展历史
第一阶段(一家独大):
计算机由美国人发明,为了让计算机能识别英文,定义了一个英文字符与数字的对应关系---ASCII码表,只记录英文字符和数字的对应关系
1Byte对应1个英文字符,1Byte=8bit,最多包含256个数字,对应着256个字符,足够表示所有英文字符
ASCII码表中:
A-Z 65-90
a-z 97-122
0-9 48-57
第二阶段(诸侯割据):
随着计算机的普及和发展,各个国家都需要将本国字符与二进制数进行转换
GBK码(国标):
记录中文、英文字符与数字的对应关系
1Byte存储英文字符,2Byte存储中文字符,如果不够用则使用 3Byte或 4Byte(2Byte最多能对应的中文字符数量为2**16=65536个)
Euc_kr:
记录韩文、英文字符与数字的对应关系
shift_JIS:
记录日文、英文字符与数字的对应关系
第三阶段(天下一统):
unicode(万国码):
记录了所有国家的字符与数字的对应关系,所有的字符最少采用2byte存储
现在的计算机可以输出所有国家的字符,内存使用的是unicode编码
由于unicode最少采用2byte,而ASCII码中1byte对应1个英文字符,因此unicode会浪费存储空间和io时间,所以又开发了一个编码(utf-8)
utf-8:
1byte存储英文字符
3byte存储中文字符
结论:
内存中的编码不需要考虑,只考虑硬盘上的即可
汉字、英文------unicode------GBK
韩文、英文------unicode------Euc_k
万国字符--------unicode-------utf-8
6.3 字符编码应用
6.3.1 编码与解码
编码(encode):将人类常用字符转换成计算机能识别的二进制数
解码(decode):将计算机的二进制数转换成人类常用的字符
标签:编码,字符,666,列表,深浅,可变,print,拷贝,id From: https://www.cnblogs.com/hbutmeng/p/18318621