3.1 变量与赋值
案例59 声明变量
导语
Python是动态语言,声明变量时不需要指定变量类型。声明变量后必须进行赋值,否则在访问变量时会引发NameError异常。
变量的类型决定于它的值,例如:
上面代码声明了四个变量,依据它们各自引用的值,变量a为整型(int),变量b为字符串类型(str),变量c为浮点数类型(float)。
在相同的代码范围内(例如在模块级别声明的变量),向同一名称的变量赋值并不会产生同名的新变量,而是让变量引用最新赋值的对象,并解除对前一个对象的引用。例如下面代码中,变量k先是引用字符串案例,当执行第二行代码后,变量k解除对字符串对象的引用,并与新的整数值建立引用关系(可称为“绑定”)。
声明变量时还可以赋值为None,它是一个内置的值,表示变量缺少有效的引用,即没有值的变量。None值所对应的Python的内置类型为NoneType。None转换为布尔类型的值为False,因此,None值可以用于判断语句中,例如:
以下代码会输出“False”。
操作流程
步骤1:声明四个变量,分别进行赋值(必须赋值)。
步骤2:依次输出上述四个变量的数据类型名称。
__class__属性可以获取指定对象的数据类型信息(type类封装类型信息),再通过__name__属性获取类型名称的字符串表示方式。__class__与__name__都属于有特殊用途的属性。
注意:并非所有对象都有__name__属性,可以先调用hasattr(obj,'__name__')进行检查,如果存在__name__属性,则hasattr函数返回True,否则返回False。
步骤3:案例代码执行后,输出结果如下:
案例60 类型批注
导语
在声明变量时,可以显式地批注其应当使用的数据类型。例如:
通过批注,表明变量num为int类型,赋值时应该使用整数值。不过,类型批注并不会影响Python语言的动态性——如果所赋的值不是int类型,在运行阶段是不会发生错误的。就像这样:
尽管批注了int类型,使用字符串类型的案例进行赋值,也不会影响代码的执行。类型批注仅用于提示开发人员,尽可能为变量设置恰当的值。
操作流程
步骤1:声明一个变量xa,并批注为str(字符串)类型,但赋值时使用int(整数)类型的值。
步骤2:在屏幕上打印xa变量实际引用的类型。
步骤3:打印当前模块中__annotations__属性的内容。
变量的批注信息会存储到__annotations__属性中,以字典的形式记录变量与所批注的类型。
步骤4:运行案例代码,屏幕输出内容如下:
案例61 声明语句也是变量赋值
导语
Python代码在执行声明函数(使用def语句)和类(使用class语句)时,实际上也进行了变量赋值。函数或类的名称就是变量的名称,而变量引用的是函数或类的类型案例。在Python中,函数(包括lambda表达式)、类型(由type类描述)、方法等都被视为对象。
例如,下面代码声明了函数test。
在模块代码运行时,无论test函数是否被调用,def关键字所在的一行(即声明语句)都会被执行,而函数体(上面例子中的pass语句就是函数体)代码只有在函数被调用时才会执行。声明语句执行后,会在当前(模块)的名称空间中生成一个名为test的变量(与test函数名称相同),并把test函数的引用赋值给test变量。此过程类似于
赋值运算符(=)右边的test是函数引用,左边是变量名称,只是变量名与函数名相同而已。因此,还可以用其他变量去引用test函数,例如:
赋值后,可以这样调用test函数
类似于为函数起一个别名。
对于类,也是相同的原理。
当案例化A类时,可以这样
so_a是个变量,它引用A类的类型对象,而so_a()会调用A类的构造函数创建A的案例,再把A的案例赋给另一个变量var。
操作流程
步骤1:声明一个函数,命名为num_sqr。
对于当前模块的名称空间而言,此时全局变量中会存在一个名为num_sqr的变量,它引用了num_sqr函数,即变量名称与函数名称相同。
步骤2:再声明一个变量,让它也引用num_sqr函数。
步骤3:分别向屏幕打印num_sqr和sqr_c变量的值。
当这两行代码执行后,屏幕上会输出以下内容:
从输出结果中可以看到,两个变量所引用的对象具有相同的内存地址,表明它们都引用了同一个案例——num_sqr函数。
步骤4:下面代码获取在全局变量中引用了num_sqr函数的所有变量。
调用globals函数返回存储全局变量的字典,并筛选出value为num_sqr对象引用的子项(通过串联的if语句)。
得到结果如下:
案例62 as关键字与赋值
在import…或者from…import…语句后面,都可以使用as关键字对导入的对象进行重新命名(分配别名)。例如:
从some_module模块中导入fun1函数,然后用as关键字分配一个别名——fun2。此过程类似于声明了新的变量fun2并让它引用fun1函数。或者为导入的模块分配一个别名
此过程相当于声明新的变量new_name,然后引用导入的some_module模块。
操作流程
步骤1:新建test_mod模块,在模块中声明trunc_str函数。
trunc_str函数的功能是截断字符串,当字符串s的长度小于或者等于n时,不做处理,将原字符串返回;当字符串s的长度大于n时,截断字符串,保留n个字符,并在新字符串的末尾加上省略号。
步骤2:在顶层代码模块中,导入trunc_str函数,然后使用as关键字分配一个别名truncateStr。
步骤3:随后通过变量truncateStr就可以访问trunc_str函数。
步骤4:运行案例代码,屏幕输出结果如下:
案例63 组合赋值法
导语
Python允许在一个赋值语句中同时初始化多个变量。例如:
相当于
实际上,此赋值行为是对元组类型(tuple)的解包,即
赋值运算符(=)右边是元组对象,一对小括号可以省略。赋值语句执行时,会从左到右依次提取出元组中的元素,逐个赋值给左边的变量列表。如上述代码中,7赋值给变量x,8赋值给变量y,9赋值给变量z。
但下面代码会发生错误。
因为用于赋值的元组有3个元素,但接收的变量只有两个,在数量上不匹配。若是希望把1赋值给变量a,然后将剩余的2、3赋值给变量b,可以这样写
赋值后,a引用整数值1,b引用了一个列表对象,列表中包含2、3两个整数值。
操作流程
步骤1:用两个浮点型数值来初始化两个变量。
步骤2:用于初始化的变量值也可以是不同类型的。
步骤3:用4个字符串对象案例化3个变量,其中,s1、s2依次引用前两个元素,sn引用剩下的两个元素。
步骤4:输出各个变量的值。
步骤5:运行案例代码,输出结果如下:
注意:sn变量使用列表类型(list)来存储剩余的两个变量值,之所以使用列表类型,是为了允许代码修改sn变量中所包含的元素。
案例64 组合赋值与表达式列表
导语
组合赋值语句的实质是在赋值运算符(=)的两边各放置一个表达式列表。常规情况下,赋值运算符两边的表达式个数应当相等,并且从左到右依次赋值。例如:
赋值运算符两边的表达式列表实为元组对象,因此,上述代码可以写成
或者,还可以这样写
使用列表(list)的格式来进行组合赋值也是允许的,例如:
以下赋值语句,左边是列表格式,右边是元组格式,这种情况也是允许的。
如果赋值运算符两边的元素个数不相等,会发生语法错误。
第一行代码中,左边有两个变量,而右边有三个值;第二行代码则相反,左边有三个变量,右边有两个值。
变量名称前可以加上星号(∗),表示该变量接收多个值。这些值是从序列对象(列表、元组等)中“解包”(unpack)出来的,然后组合为新的序列赋值给带星号的变量。请看下面代码。
上面代码先将字符“a”赋值给变量a,再把剩下的字符“b”和“c”组成新的列表赋值给变量b。把顺序反过来也是可行的。
此时,变量a前面带有星号,赋值过程为:先将最后一个字符“c”赋值给变量b,再将剩下的两个字符“a”和“b”组成新的列表赋值给变量a。
但是,同一个变量列表中不能出现多个带星号的变量,下面语句会发生错误。
因为带星号的变量所引用的值列表在元素个数上是不确定的,同时使用多个带星号的变量,会导致赋值运算符右边的值列表无法进行准确的分配。
如果赋值运算符左边只有一个变量,也是可以使用带星号的变量的,以下三条语句都是有效的。
第一行与第二行都是把变量列表组成元组对象,由于第一行中省略了小括号,所以变量后面要加上一个逗号,表示x不是单个值,而是一个元组。第三行代码中变量x表示一个列表对象。
综上所述,不管是赋值语句的左边还是右边,表达式列表可以包装为元组(tuple)和列表(list)两种序列格式。
操作流程
步骤1:声明n1、n2、n3三个变量,并进行组合赋值。
此赋值语句中,赋值运算符两边的表达式列表均为元组结构。
步骤2:声明m1、m2、m3变量,并用五个浮点数值赋值。
此语句先将数值0.1赋值给m1变量,再把0.5赋值给m3变量,剩下的0.2、0.3、0.4三个数值组成新的列表赋值给m2变量。
步骤3:将字符串案例赋值给变量q。
此语句与q='abcdefg'语句不同,如果q前面不带星号,那么q直接引用字符串案例。而上面代码中变量q前有星号,而且放到一对中括号中,表明q接收的是字符列表(即将字符串“abcdefg”,解包为由“a”“b”“c”“d”“e”“f”“g”七个单独字符组成的列表)。
步骤4:在屏幕上打印上述各个变量的值。
步骤5:运行案例代码,屏幕将输出以下内容: