变量与垃圾回收机制
# Python变量
# 变量介绍
变量使计算机能记录会变化的事物的状态。
变是变化,量是事物的状态。
例如:人有年龄,且这个年龄会变化,今年是17岁,明年是18岁,这个年龄就是变量。
# 变量定义
变量必须先定义,才能引用。
# 直接定义。
name = 888
# 引用定义,两者值的变量空间ID会相同,值的内存空间的引用计数将+1。
test = name
2
3
4
5
# 变量三大组成部分
变量名,指向等号右边的值的内存地址,内存地址用于访问其内存地址存储的值。
赋值符号,将变量值的内存地址绑定给变量名。
变量值,存储计算机分配的一个内存空间中,记录变化的事物的状态。
# 变量名的命名规范
只能由字母、数字、下划线组成。
变量名不能以数字开头。
变量名不能是Python保留的关键字,如print、and等。
命名应该见名知其意,虽然允许但不要用拼音、中文进行命名。
# 变量名的命名风格
下划线法。
单词纯小写,且单词之间用下划线分隔,常用于变量。
如:user_name、user_age。
小驼峰命名法。
第一个单词小写,后面的所有单词首字母大写,常用于变量。
如:userName、userAge。
大驼峰命名法。
所有单词首字母大写,常用于类名。
如:UserName、UserAge。
# 变量定义在内存中的过程
定义变量后,Python会向计算机申请分配一个内存空间,用于存放变量的值。
并将该值的内存地址绑定给指定的变量名,用于指向变量空间所在的位置。
# 列表定义在内存中的过程
列表类型的名称指向的内存空间中,不实际存放的值,而是存放索引与其对应的值的内存地址,内存地址再指向实际存放值的内存空间。
如果列表中引用了变量,则会将变量名所对应的值的内存空间地址,与对应索引相对应。
例如
a=[x]
就表示a的索引0指向x所指向的内存地址。
# 字典定义在内存中的过程
与列表一样,区别在于是用key来对应值的内存空间地址。
# 变量值的重要特征
id
id是变量值的号码,内存地址不同,id则不同,可以通过
id(变量名)
查看变量值的id。id是根据变量值的内存空间地址生成的号码,两个值相同的变量,其变量值id可能不同。
type
不同类型的值,需要用不同的数据类型来记录,可以通过
type(变量名)
查看变量值的数据类型。如: 字符串、数值、浮点数等。
value
即变量值的值本身。
# 变量值的比较
is关键字,比较的是变量的id是否相等,返回布尔值。
例如:
a is b
同等于id(a) == id(b)
。
==运算符,比较的是变量的值是否相等,返回布尔值。
两块不同的内存空间里,值可以相等。
# 变量的赋值
一般情况下,只要是产生了新值,就会重新申请内存空间。除了一些常用的值,如小整数池中的值、常用的字符串等等。
例如:
# 产生新值,重新申请内存空间。
a=222333
# 没有产生新值,不申请内存空间,而是绑定到a所绑定的内存空间。
b=a
# 常用的小整数池中的值,不申请内存空间。
c=1
2
3
4
5
6
7
8
# 小整数池
Python解释器启动时,会在内存中事先申请一系列用来存放常用整数的内存空间,且小整数池的值的内存空间不会被回收。
如-1、0、1、2、3、4等。
小整数池的范围是-5到256之间,但Pycharm会扩充这个范围。
我们在定义的变量时,如果值是小整数池中的值,就会直接绑定变量名到对应值的内存空间,而不会再申请新的内存空间。
这样做可以减少Python对内存空间的读写,这是解释器内部自己做的优化。
# Python常量
# 常量介绍
与变量相对,常量是不变的量。Python语法中没有常量的概念,不能定义常量,但在程序的开发过程中会涉及到常量的概念。
# 常量定义
虽然python中没有定义常量的关键字,但是有约定俗成的常量定义规范:如果变量名全为大写,则表示这是常量。
如:BASE_NUM = 1
但这只是规范性的约定,并非硬性限制。
# Python垃圾回收机制
# 垃圾回收机制介绍
定义变量申请的内存空间,如果不用了也一直不回收,而且还不断地申请,则可能导致内存溢出的问题。
而垃圾回收就是用来解决该问题用的,python的垃圾回收会将无法再被引用到的内存空间进行自动回收,这是一种内存管理优化。
# 引用计数
一个变量值的内存空间可以被多个不同的变量名引用,而变量值的内存空间被不同变量名引用的次数,就是引用计数。
# 增加引用计数例子,此时a和b同时指向10这个值的内存空间,引用计数就为2。
a=10
b=a
# 减少引用计数例子,del关键字用于解除变量名和变量值的内存空间的绑定关系。
# 此时就没有指向10这个值的变量名,引用计数就为0。
a=10
del a
2
3
4
5
6
7
8
# 垃圾与回收
垃圾,如果变量值的内存空间的引用计数为0,无法再被引用了,那么这个内存空间就是垃圾。
回收,当变量值的内存空间的引用计数为0变成垃圾时,就会自动触发Python的垃圾回收机制,回收这个无用的垃圾内存空间。
# 堆栈
Python的变量名和对象地址存储在内存中的栈区,变量值存储在内存中的堆区。
# 循环引用问题
列表L1引用列表L2,列表L2又引用列表L1,就会造成循环引用的问题。开发时要避免这种情况的出现,因为循环引用可能会导致内存泄露。
这样的情况下,即便将L1和L2的直接引用都del删掉,他们的引用计数也不会为0
因为他们的内存空间中,仍在互相间接引用彼此,所以引用计数就不会为0,也就是不会触发垃圾回收机制,但是可能会触发标记清除。
# 标记-清除
Python在程序的内存不够用时,就会触发标记清除。
它会扫描栈区中有的变量名,并将其变量名直接引用和间接引用的内存空间,标记为存活。
由栈区作为出发点,其能够访问到的直接引用和间接引用都会被标记为存活。
也就是堆区内,能被变量名直接引用,或者能被存活的内存空间引用的都会被标记存活。
而那些堆区内没有被标记的内存空间,就是无法被引用、又在占用资源的内存空间,就会被清除。
# 分代回收
基于引用计数的回收机制,每次回收内存都需要把所有对象的引用计数都遍历一遍,这非常消耗时间,所以就有了分带回收来提高效率。
分代回收的核心思想是,在历经多次扫描的情况下,存活越久的变量,GC就越会认为该变量是常用变量,GC对其扫描的频率就会降低。
也就是变量的值存活的越久,垃圾回收扫描该值的频率越低,引用计数>0就是存活。
# 引用传递
python中一切的传递,都是内存地址的传递,例如a = 1; b = a
就是把a的内存地址给了b。