
2.3.3 变量
程序运行过程中,其值可以改变的量叫变量;程序中使用的变量,属于用户自定义标识符。任何一个变量必须先命名,再赋值和引用。
一个变量实质上是指计算机内存中某个存储数据对象的单元,变量名实质上是这个数据对象存储单元在内存中的地址,该内存用来存储值。对变量进行操作就是对该存储单元进行操作,对变量赋值就是将数据对象存入该变量所代表的内存单元中,并且该内存中的值可以随意改变。
程序中的变量与数学上的变量概念不同,变量中的“变”体现的是这个存储单元可以存放不同的数据对象,但每一时刻只具有唯一的值,即新放入的数据将覆盖原有的数据。变量由变量名和变量值组成。
1.变量的声明与赋值
由于Python是一种动态类型语言,在Python中变量不需要提前声明变量名及其类型,变量的赋值操作就是变量的声明和定义的过程。每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建。声明(创建、定义)变量并赋值的语法格式如下:

变量名就是变量的标识符,使用等号(=)给变量赋值,运算符左边是一个变量名,运算符右边是该变量名指向的数据对象的值。当使用赋值语句创建一个变量后,赋值号之后的值就成为内存中的一个对象,变量名(引用)指向该对象。
在Python PEP 8编码规范中,变量名推荐使用下画线命名法,例如student_age、user_name、flag。
变量类型取决于值的类型。注意,如果只写一个变量名而没有赋值,那么Python认为这个变量没有声明。只有变量被赋值以后,该变量才会被创建并分配内存空间。
当一个变量要修改的值是数值、字符串或元组时,Python会分配一个新的内存空间存储新值,并把此对象赋值给该变量。能够原地修改的对象称为可变的,不能原地修改的对象是不可变的。
在Python中创建(声明)变量时不需要指定它的数据类型,只要给一个变量名标识符赋值就创建了该变量,所赋值的数据类型就是变量的数据类型。例如:

重新给该变量名赋值,则新的值取代原来的值,新值的数据类型就是该变量的类型,该变量的值和数据能随时改变。例如flag第1次赋值是布尔类型True,则该变量的类型是bool型(可以用type(flag)函数测试);第2次给flag赋值整数类型10,则该变量的类型是int型;第3次给flag赋值字符串类型“abc”,则该变量的类型是String型。
Python在变量声明时不需要指定数据类型,而直接对变量进行赋值,Python解释器会根据赋值或运算对象的类型在运行时自动推断变量的类型。这也是把Python语言称为动态类型的原因(动态类型可以简单地归结为对变量内存地址的分配是在运行时自动判断变量类型并对变量进行赋值)。
Python变量名本身是没有类型的,类型取决于存储的数据对象,而不是变量名,变量名只是一个指向存储对象的引用。存储数据对象时除了存储数据外还有两个头部信息,一个是类型标志符,用来说明存储对象的数据类型;另一个是引用计算器,用来标明当前存储对象有多少个引用指向它,当没有引用指向这个存储对象的时候,存储对象占用的内存空间将会被Python垃圾收集器回收。
2.给变量赋值的其他用法
(1)同时给多个变量赋值
同时定义多个变量,变量名与值用逗号隔开,一一对应。语法格式如下:

例如,执行下面的赋值语句后,a=1,b=2,c=3。

(2)交换两个变量的值
交换两个变量的值的语法格式如下:

例如,下面的语句实现交换变量a和b的值。

(3)多个变量赋相同的值
多个变量赋相同值的语法格式如下:

例如,下面的语句实现变量a、b、c都赋值10。

3.删除变量
Python的内存管理机制会跟踪所有对象,并自动删除不再使用的对象,所以一般情况下程序员不需要考虑内存管理的问题。尽管这样,删除不再使用的变量仍然是一个程序员应该保持的好习惯。删除变量的语法格式如下。

del语句删除的是变量的定义而不是数据对象。例如:

a、c变量被删除后如果再引用,它就提示该变量没有定义,b变量仍然可以访问,在IDLE中的显示结果如图2-12所示。
4.Python的内存管理机制
Python的内存管理机制包括以下三个方面。
(1)引用计数
Python中的每一个对象,都有一个指向该对象的引用计数器。如图2-13所示,首先创建一个对象12.5,然后将这个浮点数对象的引用赋值给a,因为a是第一个引用,因此,对象12.5的引用计数为1。语句b=a创建了一个指向同一个对象的引用别名b,这时并没有为b创建一个新的对象,而是将b也指向了a指向的对象,使其引用计数为2。

图2-12 删除变量的显示结果

图2-13 变量引用数据对象
下面语句的执行结果可以证明上述的说法,如图2-14所示,变量a和变量b的内存地址一致。函数id()返回指定变量所指定值的内存地址。给b赋值3后,b的内存地址改变了,而a的内存地址仍然是原来的,如图2-15所示。

图2-14 a、b两个变量引用相同的数据对象

图2-15 a、b两个变量引用不同的数据对象
从返回的内存地址可以看出,在Python中修改变量值的操作,只是修改了变量指向的数据对象的内存地址。这是因为Python采用的是基于值的内存管理方式。如果为不同变量赋值的是内存中的同一个数据对象,则这多个变量指向同一块内存地址,前面的几段代码也说明了这个原因。在Python的处理方式上,变量更像是附在数据对象上的标签(与引用的定义类似),如图2-16所示。

图2-16 数据对象上的变量标签
当变量被绑定在一个数据对象上的时候,该变量的引用计数就是1(还有另外一些情况也会导致变量引用计数的增加)。系统会自动维护这些标签,并定时扫描,当某标签的引用计数变为0的时候,该数据对象就会被回收。
(2)垃圾回收
当内存中有不再使用的对象时,垃圾收集器就会把它们清理掉。Python解释器会去检查那些引用计数为0的对象,然后清除其在内存的空间。
垃圾回收机制还有一个循环垃圾回收器,确保释放循环引用对象(a引用b,b引用a,导致其引用计数永远不为0)。
(3)内存池机制
Python还有一个内存池机制,这里不再介绍,感兴趣的读者可以查阅相关资料。