1.3 Python 3语法概要
Python 2版本已经停止更新,目前主流使用的是Python 3版本。本节简要介绍Python 3的基本语法。
值得注意的是,这里的目标不是全面而详尽地介绍Python语言的语法,而是要给初学者建立起Python 3的整体概念,避免毫无头绪、产生畏难情绪。初学时,不必太在意细节问题,需要的时候,可以到相关工具网站(4)即时查阅语法和实例。
本节只介绍Python 3语法最基本的内容。更复杂的Python 3语法,后文在首次用到时会进行简要介绍。
1.3.1 基础语法
本节讨论Python 3最基础的语法,指出初学者写出一个能运行的程序最需要注意的细节。
为了即时显示运行结果并能够保存为文件供读者参考,本书的示例一般采用Jupyter格式。
1.标识符、关键字、变量与模块导入
标识符是编程时使用的名字,用于给变量、函数等命名,作为操作对象的标识。标识符命名的要求是:第一个字符必须是字母或下画线;其他部分由字母、数字和下画线组成。标识符区分字母的大小写。
变量是程序设计的起点,用来存储各种数据。变量名是变量的标识,它的命名要符合标识符的命名要求。
在工作目录下新建一个ch1文件夹,如图1-10所示,在该文件夹下新建一个Jupyter文件,重命名为“基础语法.ipynb”。在该文件中,定义一个名为mystring的变量,并给该变量赋值字符串Hello World!,然后用print语句输出,如图1-20所示。
图1-20 标识符与关键字示例
运算符“=”用于将右边的值赋给左边的变量。
Python 3保留了35个标识符作为语法关键字,不能用于命名变量。其中的import用来导入模块,用import…from来从某个模块中导入某个函数。在“基础语法.ipynb”中,用import导入keyword模块,并列出所有关键字,如图1-20所示。
2.注释
Python用“#”开头来表示单行注释,用“'''”和“"""”成对使用表示多行注释。被注释的行会被编译器忽略掉,注释一般用来提示代码的作用等。
良好的注释对于提高代码的可读性非常重要。注释的示例见代码1-1,具体内容可见随书所附源代码程序。为了便于说明,后续示例一般不再采用截图。
代码1-1 注释示例(基础语法.ipynb)
3.代码块与缩进
一段连续执行的语句组成一个代码块,代码块一般用来完成一个具体的功能。Python语法最具特色的是用缩进来表示代码块,不像其他大多数语言用标识符来表示代码块。
缩进是指代码行前留的空格。缩进的空格数量可以不一样,但是同一个代码块的每行的缩进空格数要一样。
代码与缩进的见代码1-2。如果前三行组成一个代码块,那么第3行因为缩进的空格数不一样而编译不成功,会报错。
代码1-2 代码块与缩进示例(基础语法.ipynb)
4.数字类型及算术运算
数字(Number)类型是很直观的变量类型。Python中数字有四种类型:整数、布尔、浮点数和复数。
(1)int(整数),如1。
(2)bool(布尔),如True。
(3)float(浮点数),如1.69、9E-2。
(4)complex(复数),如1+3j、2.1+2.1j。
数字类型常用的算术运算包括+(加)、-(减)、∗(乘)、/(除)、%(取模,返回除法的余数)、∗∗(幂)和//(取整数,向下取接近商的整数),示例见代码1-3。
代码1-3 数字类型及算术运算示例(基础语法.ipynb)
5.字符串类型
前文的Hello World!“代码块”“d-e=”等都是字符串(String)。用单引号或双引号成对包围的单个或多个字符是字符串。字符串用“+”号来连接,用“∗”重复,示例见代码1-4的第3行到第6行。
代码1-4 字符串示例(基础语法.ipynb)
截取子字符串的语法为:字符串变量[头下标:尾下标:步长],遵循“左闭右开”原则,即包括头下标的字符,但不包括尾下标的字符。要注意的是,Python的下标是从0开始,负数下标表示反向计数(从后往前计数)。截取子字符串的示例见代码1-4的第8行到第13行。步长默认为1。
可以直接指明单个下标来取字符,见代码1-4的第15行到第18行。
判断子字符串是否包含在字符串中,要用到成员运算符in和not in,示例见代码1-4的第20行到第23行。
在字符串中使用特殊字符时,要用到反斜杠转义符:\。
常用的转义符有:
常用转义符的示例见代码1-4的第25行到第33行。
在字符串前加上操作符r可以禁止反斜杠发生转义,见第34、35行。
如果要在程序的运行过程中动态调整字符串,则需要用到字符串的格式化操作,最基本的格式化操作是将新值插入到字符串中格式化符的位置,如第37行到第40行所示。第37行用到了两个格式化符:%s和%d,它们分别表示在该位置插入后面括号中对应的字符串和整数。其他常用的格式化符还有表示字符的%c、表示无符号八进制的%o、表示无符号十六进制的%x、表示浮点数的%f、表示科学计数法的浮点数的%e等。
6.命令行输入
在命令行程序中,可以通过input语句等待并接受用户的输入,示例见代码1-5。
代码1-5 命令行输入示例(基础语法.ipynb)
7.一句多行和多句一行
Python通常是一行写一条语句,但如果语句太长,可以使用反斜杠\来实现一条语句写到多行。也可以把多条短语句写到一行里,语句之间用分号隔开,示例见代码1-6。
代码1-6 一句多行和多句一行示例(基础语法.ipynb)
1.3.2 数据类型与运算符
Python 3中有六个标准的数据类型,除了已经讨论过的数字和字符串类型外,还有列表(List)、元组(Tuple)、集合(Set)和字典(Dictionary)类型。
列表、元组、集合和字典都可看作是由多个元素组成的序列。下面以示例讨论它们的差别。
列表中的元素是有序的、可重复的,而集合中的元素是无序的、不可重复的,示例见代码1-7。第1行和第4行用[]定义了两个列表,虽然它们有相同的元素,但因为顺序不同,所以在第5行用等于运算符“==”进行相同比较时,会得到False的输出。第7行和第10行用{}定义了两个集合,虽然在定义时它们有不同的元素,但因为集合是无序且不可重复(自动去掉重复的元素)的,所以在第11行进行相同比较时,输出为True,即去掉重复元素并忽略元素的顺序后,两者是相同的。集合也可以用set()函数来创建。
代码1-7 列表与集合的比较示例(数据类型.ipynb)
因为列表中的元素是有序的,因此可以用指定下标的方式取出,如第13行所示;而集合中的元素是无序的,因此用同样的方法会出错,如第15行所示。
与列表和集合不同,字典中存储的元素是“键(Key):值(Value)”,其中键必须是唯一的。比如,可以定义一个字典来记录每个班的人数,见代码1-8。该示例演示了创建字典、列出指定键的值、列出字典所有键、列出字典所有值和修改指定键的值等。要注意的是,与集合相同,字典也是用大括号{}来定义的。Python规定,如果定义一个空集合,要用set()函数,因为{}创建的是一个空字典。
代码1-8 字典示例(数据类型.ipynb)
元组可以看作是元素不能修改的列表,它用小括号()来创建。
值得注意的是,除元组外,数字和字符串的值也是不可变的;列表、集合和字典的值则是可变的。
运算符用来对变量进行操作。Python运算符与其他语言的运算符基本相同,除了上一节已经讨论过的算术运算符外,还包括比较运算符、赋值运算符、位运算符、逻辑运算符、成员运算符等。
1.比较运算符
除了前文已经用过的等于运算符==外,比较运算符(也称关系运算符)还有不等于运算符!=、大于运算符>、小于运算符<、大于或等于运算符>=和小于或等于运算符<=。比较运算的结果是布尔值True或False。
2.赋值运算符
除了前文已经用过的简单赋值运算符=外,还有加法赋值运算符+=、减法赋值运算符-=、乘法赋值运算符∗=、除法赋值运算符/=、取模赋值运算符%=、幂赋值运算符∗∗=和取整除赋值运算符//=。
加法赋值运算符的应用见代码1-9的第1行到第4行。其他赋值运算符的含义类似。
代码1-9 运算符示例(数据类型.ipynb)
3.位运算符
位运算符把数字转换为二进制来进行计算。位运算符有按位与运算符&、按位或运算符|、按位异或运算符^、按位取反运算符~、左移运算符<<和右移运算符>>。
位运算符示例见代码1-9的第6行到第9行。2的二进制表示为:0000 0010,5的二进制表示为:0000 0101,它们按位与和按位或的结果分别为0000 0000和0000 0111,即十进制的0和7。其他位运算符的含义类似。
4.逻辑运算符
逻辑运算符有与运算符and、或运算符or和非运算符not。
对于与运算逻辑表达式:x and y,如果x为False,返回False;否则它返回y的计算值,示例见代码1-9的第11行到第18行。
对于或运算逻辑表达式:x or y,如果x是非0,它返回x的值;否则它返回y的计算值。
对于非运算逻辑表达式:not x,如果x为True,返回False;如果x为False,它返回True,示例见代码1-9的第19行到第22行。
5.成员运算符
对于字符串、列表、元组、集合和字典等序列类型,可以用成员运算符来判断是否能在序列中找到指定的元素。成员运算符只有两个:in和not in,分别表示找到和找不到。示例见代码1-9的第24行到第35行。
运算符的优先级从高到低如表1-1所示。用括号能够改变默认的优先级,用括号也可以改善代码的可读性。
表1-1 运算符的优先级
1.3.3 函数
函数是完成某个功能的代码段,可被其他代码调用。调用的代码可以将数据传递给函数,函数可将对数据的处理结果返回给调用代码。
函数用def关键字来定义,其后接函数名称标识符和小括号对()。传入的数据称为参数,放在小括号中。小括号后接冒号“:”,表示函数内容开始,函数内容另起一行并缩进。函数以return语句结束,如果有返回值,则将返回值接在return关键字后面。其他代码在调用时,按照函数定义的参数顺序依次将数据传入函数,或者用赋值符号指定各个参数的值。函数定义和调用的示例见代码1-10。第1行到第3行定义了一个减法函数,它的第2个参数b称为默认参数,其默认值为0,默认参数在调用时可以不输入新值,见第10行。
代码1-10 函数示例(函数.ipynb)
定义在函数内部的变量只能在函数内部被访问,称为局部变量;定义在函数外部的变量可在全局范围内被访问,称为全局变量。全局变量默认不能在函数内部被访问。局部变量和全局变量的示例见代码1-10的第14行到22行。第14行和第16行各定义了一个同名的变量,但它们作用域的不同。虽然局部变量在函数内部改变了值,但不影响同名的全局变量。
当把数字、字符串和元组这些不可变的数据类型作为函数的参数传递时,它们的值也是不可变的,也就是说,即使函数内部的代码对它的值进行了修改,但在函数外部它仍然是原来的值。示例见代码1-10的第24行到32行。
而列表、集合和字典这些可变的数据类型作为函数的参数传递时,它们的值可以被函数内部的代码改变,见代码1-10的第33行到39行。
当函数内容很简单时,可以使用lambda表达式来代替函数定义,称为匿名函数。Lambda函数只包含一个语句,它的定义非常简洁。代码1-10中,第1行到第3行定义的函数可用第41行的lambda函数代替。
Python将一些常用的功能封装成了内置函数,可以直接调用。用dir内置函数可以查看所有的内置函数名,见代码1-11的第1行。用help内置函数可以查看内置函数的使用说明,见第11行。
代码1-11 内置函数(函数.ipynb)
前面用过的print、input、dir、help函数都是内置函数。常用的内置函数包括数学运算、集合操作和I/O操作等类别。
数学运算类内置函数有求绝对值函数abs、求幂函数pow、求四舍五入函数round、对集合求和函数sum、取商和余数函数divmod、创建复数函数complex、产生一个序列函数range、转换成浮点数函数float、转换成整数函数int、转换为八进制函数oct、转换为十六进制函数hex、转换为二进制函数bin、转换为布尔类型函数bool等。
集合操作类内置函数有创建字典函数dict、创建集合函数set、创建元组函数tuple、转换为列表函数list、生成一个迭代器函数iter、排序函数sorted、返回集合中的最大值函数max、返回集中的最小值函数min、返回集合长度函数len、遍历元素执行操作函数map、转换为字符串函数str、格式化输出字符串函数format等。
I/O操作类内置函数除了print和input外,还有创建文件的file函数和打开文件的open函数等。
其他常用内置函数还有对类和对象进行操作的函数(将在下文讨论)以及返回变量类型函数type等。这些函数的具体用法可在需要时用help函数或到相关网站查阅。
1.3.4 类和对象
前文简要讨论了面向对象的程序设计思想。面向对象的程序设计方法具体包括创建类、实例化、继承、重载等,它们是实现面向对象程序设计思想的重要手段。Python从设计之初就是一门面向对象的语言,它支持实现面向对象的各类方法。
这里不准备全面讨论面向对象程序设计方法,仅介绍在sklearn、TensorFlow和MindSpore等扩展库和框架下实现机器学习和深度学习等应用所必需的基本知识,包括对象的使用和类的创建。
1.对象的使用
实际上,在Python里,一切皆为对象,比如整数1106、字符串mbp、列表[1,‘abc’,5.29]等,都是对象。
前文讨论过,对象有属性和行为,属性可以表示对象的特征和状态,行为表示对象的功能。具有相同类型属性和行为的对象,用一个“类(Class)”来抽象,比如用来定义字符串变量的内置字符串类型,实际上就是一个名为str的类。str类描述了所有字符串(对象)的属性类型和行为。在Python里,把类的行为的实现称为方法,方法的定义和应用类似于函数。用dir()内置函数可以查看str类的所有方法,见代码1-12第1行。以双下画线__开头且结尾的方法__xxx__,是专有方法。如第6行所示的__dir__专有方法默认是列出类的所有方法,实际上,dir()内置函数就是调用了类的__dir__专有方法。
代码1-12 类和对象示例(类和对象.ipynb)
用help()内置函数可以查看方法的解释和使用提示,见第14行,可见str类的strip方法是去掉原字符串头尾的空格。
一个类是某一类对象的抽象,因此,该类对象的创建要依据该类来创建,称为实例化。第28行创建了一个str类的对象,该对象的值为aaaa。因为str类是内置的,Python提供了更加方便和直观的对象创建方法,即已经多次使用过的赋值方式。第29行也创建了一个值为aaaa的字符串对象。
作为内置类型,可以用比较运算符==来比较两个字符串是不是相等,见第30行。也可以采用调用专有方法__eq__()来进行比较,第32行使用点号“.”来调用(访问)对象的方法(属性)。实际上,比较运算符也是通过调用专有方法__eq__()来实现比较。
Python中的对象由对象标识符(Identity)、类型(Type)和值(Value)组成。第28行创建的字符串对象,它的标识符为a,类型为str,值为aaaa。
Python的机器学习扩展库将机器学习的算法已经封装为各种类。使用时,只需要将类实例化,并按使用要求使用即可。下文在讨论各机器学习算法时,将直接介绍它们的使用方法。
除了str类,Python的内置类型实际上也都封装成了类,下面介绍它们的常用方法。
字符串类str的常用方法有:在字符串中查找指定子串方法find、字符串格式化方法format、检查是否只包含十进制数字方法isdecimal、检查是否只包含数字方法isdigit、检查是否只包含空格方法isspace、合并字符串方法join、子串替换方法replace、去掉头尾空格方法strip、分隔字符串方法split等。strip方法的应用示例见代码1-13的第1行到第3行。
代码1-13 内置类型的常用方法示例(类和对象.ipynb)
整数类int、浮点数类float、复数类complex和布尔类bool的方法主要是支持各类运算符的专有方法,如加法__add__、减法__sub__等。
列表类型的list类的常用方法有:向列表的末尾添加元素方法append、清除列表方法clear、复制列表方法copy、返回子元素出现的次数方法count、返回子元素的索引方法index、在指定索引处插入元素方法insert、删除列表中一个元素方法pop、从列表中删除指定元素方法remove、反转列表方法reverse、列表元素排序方法sort等。Reverse方法应用示例见代码1-13的第8行到第11行。
因为元组是不可变的序列,所以元组类型的tuple类没有可对元素进行修改的方法,其他方法与list类相似。
集合类型的set类的常用方法有:添加元素方法add、移除所有元素方法clear、复制集合方法copy、删除集合中指定元素方法discard、返回集合交集方法intersection、随机移除元素方法pop、移除指定元素方法remove、返回两个集合的并集方法union等。
字典类型的dict类的常用方法有:删除字典内所有元素方法clear、返回指定键的值方法get、判断键是否在字典中的方法key_in_dict、删除指定键对应的元素方法pop等。
2.类的创建
如上文所述,对象是类的实例化。sklearn等扩展库中,将机器学习算法已经封装成为类了,在使用时,只需要实例化它们,得到相应对象。但是,如果要发展新算法或调整原有算法,就需要创建新的类。
在TensorFlow 2和MindSpore等深度学习框架中,在构建神经网络时,有时需要创建新的类。
在机器学习和深度学习的应用中,类的创建和使用主要涉及类定义、构造方法、继承和方法重写等,下面用一个简单的示例来说明,见代码1-14。
代码1-14 类的创建与继承(类和对象.ipynb)
该示例先创建了一个用来计算圆面积的类circlar_area,它有一个属性(圆周率)和两个方法(构造方法__init__和计算面积的方法compute)。
从该类继承了一个用来计算圆环面积的子类round_area。子类的构造函数中,要调用父类的构造函数,见第14行。
子类重写了父类的方法compute,以实现计算圆环面积,见第16行。
1.3.5 流程控制
程序在执行语句时,一般按语句排列的顺序依次执行。
流程控制是指某条语句根据条件在一些候选代码块中选择某一个代码块作为下一步执行的对象。Python支持两种基本的流程控制结构:分支结构和循环结构。
1.分支结构
分支结构根据条件来选择性执行代码块。分支结构主要用if语句来实现。
代码1-15 if语句的三种形式
if语句的第一种形式见代码1-15的第1行和第2行,如果condition代表的表达式为True,则在程序执行过程中要执行statements代表的代码块;否则,不执行statements所代表的代码块,直接执行后续的代码块,如图1-21(a)所示。
if语句的第二种形式见代码1-15的第4行到第7行,它是引入了else关键字来提供另一条执行路径。如果condition代表的表达式为True,则在程序执行过程中要执行statements1代表的代码块;否则,执行statements2所代表的代码块,然后再执行后续的代码块,如图1-21(b)所示。
if语句的第三种形式见代码1-15的第9行到第14行,它通过引入elif关键字来提供再一次条件判断,执行路径再次分岔。判断和执行过程如图1-21(c)所示。还可以通过elif来多次进行条件判断,使执行路径多次分岔。
if语句三种形式的示例见代码1-16。
图1-21 if语句执行流程
代码1-16 if语句示例(流程控制.ipynb)
2.循环结构
循环结构根据条件来重复执行代码块。Python中的循环语句有while语句和for语句。
代码1-17 循环结构语句
while语句见代码1-17的第1行和第2行,while关键字后是条件condition。当条件condition满足(为True)时,代码块statements会被重复执行,直到condition不满足才会执行后续的代码块。执行流程见图1-22(a)。while语句示例见代码1-18的第1行到第10行。该示例用while语句来打印一系列的整数,它的condition是a<=10,当该条件满足时,下面的代码块会被执行。下面代码块里有改变a值的语句,因此,每执行一次代码块,a的值都会加2,于是在第5次后,a的值成了12,此时condition不再满足,于是结束循环。
图1-22 while语句和for语句执行流程
while语句还有另一种形式,就是像if语句一样在后面用else关键字加上一段在退出循环时执行的代码块,见代码1-17的第4行到第7行。
for语句见代码1-17的第9行和第10行。需要注意的是,for语句只用于遍历序列,for关键字后是一个变量var,in关键字后是一个序列sequence。在执行时,var要遍历序列中的所有元素,var每取一个序列中的元素,下面的代码块statements要执行一次。for语句的执行流程见图1-22(b)。
for语句也可以在后面用else关键字加上一段在退出循环时执行的代码块,见代码1-17的第12行到第15行。
代码1-18 循环结构语句示例(流程控制.ipynb)
for语句应用示例见代码1-18的第12行到第22行。在该例中,序列sequence是一个列表,每次从列表中取出一个值赋予变量i,然后执行下面的代码块。列表中所有的值取完后,会执行else关键字后的代码块,再结束循环。
除了列表,集合、字符串、字典、元组等数据类型都可以作为序列sequence。常用内置的range()函数来生成数列,如可用range(2,11,2)来产生2,4,6,8,10数字序列。
循环结构中常用break语句来结束循环,在语句的执行过程中,只要遇到break语句,就直接执行后续的语句。
循环结构中可用continue语句来结束当前循环,进入下一循环。在while语句中,continue的作用就是重新开始条件condition的判断;在for语句中,continue的作用就是在序列suquence中取下一值。
1.3.6 常用扩展库
本书涉及的Python扩展库有:
1.NumPy
NumPy扩展库提供了对数组的支持。在机器学习算法的实践中,样本集一般都看作数组进行操作处理,因此该扩展包在机器学习的应用中有很重要的作用。
2.Pandas
Pandas是面板数据(Panel Data)的简写,它是Python的数据分析工具,支持类似SQL的数据增、删、改、查功能。Pandas结合NumPy,可以完成大部分的数据准备工作。
3.SciPy
SciPy包提供对矩阵计算的支持。有些机器学习算法涉及矩阵计算。
4.Scikit-Learn(sklearn)(5)
Scikit-Learn是一个基于Python的机器学习扩展包,它包含六个部分:分类、回归、聚类、数据降维(Dimensionality Reduction)、模型选择(Model Selection)和数据预处理(Preprocessing)。它在学术界和工程界都得到了广泛的应用。在学习相关知识时,不仅可以参考它提供的丰富资源,还可利用它提供的各种工具来快速完成试验,同时它还提供了算法实现的源代码供学习。
5.Matplotlib
Matplotlib主要用于绘图和绘表,是数据可视化工具,在Python程序中经常用来作图示说明。它的官网提供了很多示例(6),读者可以根据图形提示,选择相应的代码进行修改,以适合自己的需要。本书主要用Matplotlib来画折线图和点图,相关内容不难学习,读者可以参考一些中文学习网站(7)。
6.SymPy
SymPy主要用于符号计算,如公式推导等,涉及的领域包括:代数运算、多项式、微积分、方程式求解、离散数学、矩阵、几何、物理学、统计学、画图、打印等。
后文在涉及这些库时,会根据需要作进一步介绍。