封面
前言
-
“Python”中文翻译“蟒蛇”
-
Python的优点:
- 简单、易学、免费、开源:Python简单、易学。我们可以自由发布其复制版本,阅读、修改其源代码,将其(部分)用于新软件中。
- 解释型:Python是边解释边执行的,Python解释器会将源代码转换为中间字节码形式,然后将其解释为机器语言并执行。
- 可移植:Python解释器已被移植在许多平台上,Python程序无须经过修改就可以在多个平台上运行。
- 代码规范:Python所采用的强制缩进的方式,使得其代码具有极佳的可读性。
- 面向对象:与C++和Java等相比,Python以强大而简单的方式实现了面向对象编程。
- 胶水语言:标准版本的Python调用C语言,并可以借助C语言的接口驱动调用所有编程语言。
- 丰富的库:Python的标准库种类繁多,可以帮助处理各种工作,我们不需要安装就可以直接使用这些库。
- 动态类型:Python不会检查数据类型,在声明变量时不需要指定数据类型。
编写和运行Python程序方式:
- 交互方式
- 文件方式
1.交互方式
Python安装包提供了交互式运行工具——Python Shell,在安装好Python后,我们就可以单击Windows“开始”菜单打开Python 3.x了。
2.文件方式
通过文件方式编写和运行Python程序时,首先需要编写Python代码,然后在Windows下启动命令提示符,再使用Python指令运行Python代码文件。
编写Python代码时,既可以使用任意一种文本编辑工具,也可以使用专业的IDE(Integrated Development Environments,集成开发环境)工具。
一些基础概念
标识符
标识符就是变量、函数、属性、类、模块等可以由程序员指定名称的代码元素。构成标识符的字符均遵循一定的命名规则。
注:Unicode是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。
- Python中标识符的命名规则:
- 区分大小写:Myname与myname是两个不同的标识符。
- 首字符可以是下画线(_)或字母,但不能是数字。
- 除首字符外的其他字符必须是下画线、字母和数字。
- 关键字不能作为标识符。
- 不要使用Python的内置函数作为自己的标识符。
关键字
关键字是由语言本身定义好的有特殊含义的代码元素
变量
在Python中为一个变量赋值的同时就声明了该变量,该变量的数据类型就是赋值数据所属的类型,该变量还可以接收其他类型的数据。
·注:Python中,声明变量后,也可以接受其他类型的数据。这是与Java和C等编程语言的区别。
语句
Python代码是由关键字、标识符、表达式和语句等构成的,语句是代码的重要组成部分。在Python中,一行代码表示一条语句,在一般情况下语句结束时不加分号。
代码注释
在使用#(井号)时,#位于注释行的开头,#后面有一个空格,接着是注释的内容。
模块
在Python中一个模块就是一个文件,模块是保存代码的最小单位,在模块中可以声明变量、函数、属性和类等Python代码元素。
- 一个模块访问另一个模块中的代码元素——三种导入语句:
- ①import<模块名>:通过这种方式会导入m2模块的所有代码元素,在访问时需要加前缀“m2.”
- ②from<模块名>import<代码元素>:通过这种方式会导入m2中的x变量,在访问时不需要加前缀“m2.”
- ③from<模块名>import<代码元素>as<代码元素别名>:与②类似,在当前m1模块的代码元素(x变量)与要导入的m2模块的代码元素(x变量)名称有冲突时,可以给要导入的代码元素(m2中的x)一个别名x2
数字类型的数据
在Python中有6种主要的内置数据类型:数字、字符串、列表、元组、集合和字典。列表、元组、集合和字典可以容纳多项数据,在本书中把它们统称为容器类型的数据。
Python中的数字类型有4种:整数类型、浮点类型、复数类型和布尔类型。需要注意的是,布尔类型也是数字类型,它事实上是整数类型的一种。
Python中的整数类型为int类
示例:
Python的浮点类型为float类,浮点类型主要用来存储小数数值
示例:
Python的复数类型
示例:
Python的布尔类型为bool类
bool是int的子类,他只有两个值:True和Flase
示例:
数字类型的相互转换
在Python的数字类型中,除复数隐式类型的转换数字之间可以进行数学计算,在进行数学计算时若数字类型不同,则会发生隐式类型的转换。
隐式类型的转换
数字之间可以进行数学计算,在进行数学计算时若数字类型不同,则会发生隐式类型的转换
示例:
显式类型的转换
示例:
运算符
我们可以通过运算符把数据连接起来,形成表达式,进而通过表达式进行运算,最后返回一个结果。
算数运算符
算术运算符用于组织整数类型和浮点类型的数据,有一元运算符和二元运算符之分。
一元算术运算符有两个:+(正号)和-(负号),例如:+a还是a,-a是对a的取反运算。
二元算术运算符如表所示。
·注:True被当作整数1进行运算,False被当作整数0进行运算。在操作数中如有浮点数字,则表达式的计算结果也是浮点类型。
比较运算符
比较运算符用于比较两个表达式的大小,其结果是布尔类型的数据,即True或False。
·注:比较运算符可用于任意类型的数据,但参与比较的两种类型的数据要相互兼容,即能进行隐式转换。例如:整数、浮点数和布尔这三种类型是相互兼容的。
逻辑运算符
逻辑运算符用于对布尔型变量进行运算,其结果也是布尔型。
·注:Python也采用了“短路”设计。
即
位运算符
位运算是以二进位(bit)为单位进行运算的,操作数和结果都是整数类型的数据。
或运算:
与运算:
异或运算:
取反运算(原码、补码、反码):
赋值运算符
补充:进制的前缀与后缀 |
运算符的优先级
程序流程控制
控制程序的流程,使得程序具有“判断能力”,能够像人脑一样分析问题。
分支语句(条件语句)
if语句三种结构:
if结构
if-else结构
if-elif-else结构
if结构
语法:
·注:1个Tab=4个半角空格
示例:
if-else结构
语法:
示例:
if-elif-else结构
语法:
示例:
循环语句
Python支持两种循环语句:while和for。
while语句
语法:
示例:
while-else语句
示例:
for语句
语法:
示例:
for-else语句
示例:
跳转语句
跳转语句能够改变程序的执行顺序,包括break、continue和return。break和continue用于循环体中,而return用于函数中。
break语句
break语句用于强行退出循环体,不再执行循环体中剩余的语句。
示例:
continue语句
continue语句用于结束本次循环,跳过循环体中尚未执行的语句,接着进行终止条件的判断,以决定是否继续循环。
示例:
循环语句实践:计算水仙花数
水仙花数是一个三位数,三位数各位的立方之和等于三位数本身。
关于else的注意事项
在循环体正常结束时会执行else语句,如果发生中断,则不运行else语句 |
容器类型的数据
Python内置的数据类型如序列(列表、元组等)、集合和字典等可以容纳多项数据,我们称它们为容器类型的数据。
序列
序列(sequence)是一种可迭代的、元素有序的容器类型的数据。
序列包括列表(list)、字符串(str)、元组(tuple)和字节序列(bytes)等。
序列的索引操作
序列中的元素都是有序的,每一个元素都带有序号,这个序号叫作索引。索引有正值索引和负值索引之分。
我们是通过下标运算符访问序列中的元素的,下标运算符是跟在容器数据后的一对中括号([]),中括号带有参数,对于序列类型的数据,这个参数就是元素的索引序号。
示例:
加和乘操作
加(+)和乘()运算符也可以用于序列中的元素操作。加(+)运算符可以将两个序列连接起来,乘()运算符可以将两个序列重复多次。
示例:
切片操作
序列的切片(Slicing)就是从序列中切分出小的子序列。
切片运算符的语法形式为[start:end:step]。其中,start是开始索引,end是结束索引,step是步长(切片时获取的元素的间隔,可以为正整数,也可以为负整数)。
注意:切下的小切片包括start位置的元素,但不包括end位置的元素,start和end都可以省略。
示例:
成员测试
成员测试运算符有两个:in和not in,in用于测试是否包含某一个元素,not in用于测试是否不包含某一个元素。
示例:
列表
列表(list)是一种可变序列类型,我们可以追加、插入、删除和替换列表中的元素。
创建列表
创建列表有两种方法。
1 list(iterable)函数:参数iterable是可迭代对象(字符串、列表、元组、集合和字典等)。
2 [元素1,元素2,元素3,⋯]:指定具体的列表元素,元素之间以逗号分隔,列表元素需要使用中括号括起来。
示例:
追加元素
列表是可变的序列对象,列表可以追加元素。
1 在列表中追加单个元素时,可以使用列表的append(x)方法。
2 在列表中追加多个元素时,可以使用加(+)运算符或列表的extend(t)方法。
·区分:
示例:
插入元素
想向列表中插入元素时,可以使用列表的list.insert(i,x)方法,其中,i指定索引位置,x是要插入的元素。
示例:
替换元素
想替换列表中的元素时,将列表下标索引元素放在赋值符号(=)的左边,进行赋值即可。
示例:
删除元素
想在列表中删除元素时,可使用列表的list.remove(x)方法,如果找到匹配的元素x,则删除该元素,如果找到多个匹配的元素,则只删除第一个匹配的元素。
示例:
元组
元组(tuple)是一种不可变序列类型。
创建元组
创建元组时有两种方法。
1 tuple(iterable)函数:参数iterable是可迭代对象(字符串、列表、元组、集合和字典等)。
2 (元素1,元素2,元素3,⋯):指定具体的元组元素,元素之间以逗号分隔。对于元组元素,可以使用小括号括起来,也可以省略小括号。
示例:
元组拆包
创建元组,并将多个数据放到元组中,这个过程被称为元组打包。
与元组打包相反的操作是拆包,就是将元组中的元素取出,分别赋值给不同的变量。
示例:
集合
集合(set)是一种可迭代的、无序的、不能包含重复元素的容器类型的数据。
创建集合
我们可以通过以下两种方式创建集合。
1 set(iterable)函数:参数iterable是可迭代对象(字符串、列表、元组、集合和字典等)。
2 {元素1,元素2,元素3,⋯}:指定具体的集合元素,元素之间以逗号分隔。对于集合元素,需要使用大括号括起来。
示例:
修改集合
修改集合类似于修改列表,可以向其中插入和删除元素。修改可变集合有如右所示的常用方法。
add(elem):添加元素,如果元素已经存在,则不能添加,不会抛出错误。
remove(elem):删除元素,如果元素不存在,则抛出错误。
clear( ):清除集合
示例:
字典
字典(dict)是可迭代的、通过键(key)来访问元素的可变的容器类型的数据。
字典由两部分视图构成:键视图和值视图。键视图不能包含重复的元素,值视图能。在键视图中,键和值是成对出现的。
创建字典
我们可以通过以下两种方法创建字典。
1 dict( )函数。
2 {key1:value1,key2:value2,...,key_n:value_n}:指定具体的字典键值对,键值对之间以逗号分隔,最后用大括号括起来。
示例:
修改字典
字典可以被修改,但都是针对键和值同时操作的,对字典的修改包括添加、替换和删除。
示例:
访问字典视图
我们可以通过字典中的三种方法访问字典视图。
items( ):返回字典的所有键值对视图。
keys( ):返回字典键视图。
values( ):返回字典值视图。
解释:
第2行:返回字典的所有键值对视图dict_items
第4行:dict_items可以使用list( )函数返回键值对列表
第6行:返回字典键视图dict_keys
第8行:dict_keys可以使用list( )函数返回键列表
第10行:返回字典值视图dict_values
第12行:dict_values可以使用list( )函数返回值列表
遍历字典
什么是遍历
示例:
字符串
字符串的表示方式
字符串有三种表示方式:普通字符串、原始字符串和长字符串。
普通字符串
普通字符串指用单引号(')或双引号(")括起来的字符串。
字符转义
常用转义符:
示例:
原始字符串
示例:
长字符串
如果要使用字符串表示一篇文章,其中包含了换行、缩进等排版字符,则可以使用长字符串表示。对于长字符串,要使用三个单引号(''')或三个双引号(""")括起来。
示例:
字符串与数字的相互转换
字符串和数字是不兼容的两种数据类型,不能进行隐式转换,只能通过函数进行显式转换。
将字符串转换为数字
将字符串转换为数字,可以使用int( )和float( )实现,如果成功则返回数字,否则引发异常。
示例:
在默认情况下,int( )函数都将字符串参数当作十进制数字进行转换,所以int('AB')会失败。int( )函数也可以指定基数(进制)。
将数字转换为字符串【str()】
将数字转换为字符串,可以使用str( )函数,str( )函数可以将很多类型的数据都转换为字符串。
示例:
格式化字符串【format()】
使用占位符
要想将表达式的计算结果插入字符串中,则需要用到占位符。对于占位符,使用一对大括号({})表示。
默认占位符
参数序号占位符
参数名占位符
示例:
格式化控制符
在占位符中还可以有格式化控制符,对字符串的格式进行更加精准的控制
格式化控制符位于占位符索引或占位符名字的后面,之间用冒号分隔,语法:{参数序号:格式控制符}或{参数名:格式控制符}。
字符串的格式化控制符及其说明如下表所示:
示例:
操作字符串
字符串查找
字符串的find( )方法用于查找子字符串。该方法的语法为str.find(sub[,start[,end]]),表示:在索引start到end之间查找子字符串sub,如果找到,则返回最左端位置的索引;如果没有找到,则返回-1。
注:在Python文档中[]表示可以省略部分内容,find()方法的参数[,start[,end]]表示start和end都可以省略
示例:
字符串替换
若想进行字符串替换,则可以使用replace( )方法替换匹配的子字符串,返回值是替换之后的字符串。该方法的语法为str.replace(old,new[,count]),表示:用new子字符串替换old子字符串。count参数指定了替换old子字符串的个数,如果count被省略,则替换所有old子字符串。
示例:
字符串分割
若想进行字符串分割,则可以使用split( )方法,按照子字符串来分割字符串,返回字符串列表对象。该方法的语法为str.split(sep=None,maxsplit=-1),表示:使用sep子字符串分割字符串str。maxsplit是最大分割次数,如果maxsplit被省略,则表示不限制分割次数。
示例:
实践:统计英文文章中单词出现的频率
函数
定义函数
自定义函数的语法格式如下:
形式参数
由于定义函数时的参数不是实际数据,会在调用函数时传递给他们是实际数据,所以我们定义函数时的参数成为形式参数,简称形参;称调用函数时传递的实际数据为实际参数,简称实参。可以将形参理解为在函数中定义的变量。
示例:
调用函数
使用位置参数调用函数
在调用函数时传递的实参与定义函数时的形参顺序一致,这是调用函数的基本形式。
示例:
使用关键字参数调用函数
在调用函数时可以采用“关键字=实参”的形式,其中,关键字的名称就是定义函数时形参的名称。
示例:
参数的默认值
示例:
可变参数
基于元组的可变参数(*可变参数)
*可变参数在函数中被组装成一个元组。
示例:
基于字典的可变参数(**可变参数)
**可变参数在函数中被组装成一个字典。
示例:
函数中变量的作用域
变量可以在模块中创建,作用域(变量的有效范围)是整个模块,被称为全局变量。变量也可以在函数中创建,在默认情况下作用域是整个函数,被称为局部变量。
示例:
函数类型
Python中的任意一个函数都有数据类型,这种数据类型是function,被称为函数类型。
理解函数类型
函数类型的数据与其他类型的数据是一样的,任意类型的数据都可以作为函数返回值使用,还可以作为函数参数使用。因此,一个函数可以作为另一个函数返回值使用,也可以作为另一个函数参数使用。
示例:
add( )和sub( )函数有两个数字参数,具有相同的函数类型。square( )函数只有一个数字参数,所以square( )与add( )、sub( )函数的类型不同。
数据处理的函数
在Python中定义了一些用于数据处理的函数,如filter( )和map( )等。我们先介绍filter( )函数。
过滤函数filter( )
filter( )函数用于对容器中的元素进行过滤处理。
filter( )函数的语法如下:
参数function是一个提供过滤条件的函数,返回布尔值。
参数iterable是容器类型的数据。
示例:
映射函数map( )
map( )函数用于对容器中的元素进行映射(或变换)。例如:我想将列表中的所有元素都乘以2,返回新的列表。
map( )函数的语法如下:
参数function是一个提供变换规则的函数,返回变换之后的元素。
参数iterable是容器类型的数据。
示例:
lambda( )函数
我们之前学习的函数都是有名称的函数,例如在8.1节定义的rect_area( )函数,rect_area就是其函数名。我们也可以定义匿名函数,匿名函数就是没有名称的函数。
在Python中使用lambda关键字定义匿名函数。lambda关键字定义的函数也被称为lambda( )函数,定义lambda( )函数的语法如下。
“参数列表”与函数的参数列表是一样的,但不需要用小括号括起来
lambda( )函数与有名称的函数一样,都是函数类型。add( )和sub( )函数可以被lambda( )函数替代。
示例:
类与对象
类和对象都是面向对象中的重要概念。面向对象是一种编程思想,即按照真实世界的思维方式构建软件系统。
定义类
Python中的数据类型都是类,我们可以自定义类,即创建一种新的数据类型。Python中类的定义语法格式如图所示。
示例:
小汽车(Car)类继承了object类,object类是所有类的根类,在Python中任何一个类(除object外)都直接或间接地继承了object,直接继承object时(object)部分的代码可以省略。
pass语句的作用:pass语句只用于维持程序结构的完整。我们在编程时若不想马上编写某些代码,又不想有语法错误,就可以使用pass语句占位。
创建对象
类相当于一个模板,依据这样的模板来创建对象,就是类的实例化,所以对象也被称为“实例”。
示例:
类的成员
分类:
成员变量也被称为数据成员,保存了类或对象的数据。例如,学生的姓名和学号。
构造方法是一种特殊的函数,用于初始化类的成员变量。
成员方法是在类中定义的函数。
属性是对类进行封装而提供的特殊方法。
实例变量
实例变量就是对象个体特有的“数据”,例如狗狗的名称和年龄等。
示例:
构造方法
类中的__init__( )方法是一个非常特殊的方法,用来创建和初始化实例变量,这种方法就是“构造方法”。在定义__init__( )方法时,它的第1个参数应该是self,之后的参数用来初始化实例变量。调用构造方法时不需要传入self参数。
示例:
实例方法
实例方法与实例变量一样,都是某个实例(或对象)个体特有的方法。
定义实例方法时,它的第1个参数也应该是self,这会将当前实例与该方法绑定起来,这也说明该方法属于实例。在调用方法时不需要传入self,类似于构造方法。
示例:
类变量
类变量是属于类的变量,不属于单个对象。
例如,有一个Account(银行账户)类,它有三个成员变量:amount(账户金额)、interest_rate (利率)和owner(账户名)。amount和owner对于每一个账户都是不同的,而interest_rate对于所有账户都是相同的。amount和owners是实例变量,interest_rate是所有账户实例共享的变量,它属于类,被称为“类变量”。、
类方法
类方法与类变量类似,属于类,不属于个体实例。在定义类方法时,它的第1个参数不是self,而是类本身。
示例:
注:
封装性
封装性是面向对象重要的基本特性之一。封装隐藏了对象的内部细节,只保留有限的对外接口,外部调用者不用关心对象的内部细节,使得操作对象变得简单。
例如,一台计算机内部极其复杂,有主板、CPU、硬盘和内存等,而一般人不需要了解它的内部细节。计算机制造商用机箱把计算机封装起来,对外提供了一些接口,如鼠标、键盘和显示器等,使用计算机就变得非常简单。
私有变量
为了防止外部调用者随意存取类的内部数据(成员变量),内部数据(成员变量)会被封装为“私有变量”。外部调用者只能通过方法调用私有变量。
在默认情况下,Python中的变量是公有的,可以在类的外部访问它们。如果想让它们成为私有变量,则在变量前加上双下画线(__)即可。
示例:
由于在类的外部不可以访问私有变量,因此上述代码在运行时会发生错误。
私有方法
私有方法与私有变量的封装是类似的,在方法前加上双下画线(__)就是私有方法了。
示例:
由于在类的外部不可以访问私有方法,因此上述代码在运行时会发生错误.
使用属性
为了实现对象的封装,在一个类中不应该有公有的成员变量,这些成员变量应该被设计为私有的,然后通过公有的set (赋值)和get(取值)方法访问。
示例:
属性在本质上就是两个方法,在方法前加上装饰器使得方法成为属性。属性使用起来类似于公有变量,可以在赋值符(=)左边或右边,左边被赋值,右边取值。
继承性
继承性也是面向对象重要的基本特性之一。
9.6.1 Python中的继承
在Python中声明子类继承父类,语法很简单,定义类时在类的后面使用一对小括号指定它的父类就可以了。
示例:
多继承
在Python中,当子类继承多个父类时,如果在多个父类中有相同的成员方法或成员变量,则子类优先继承左边父类中的成员方法或成员变量,从左到右继承级别从高到低。
示例:
方法重写
如果子类的方法名与父类的方法名相同,则在这种情况下,子类的方法会重写(Override)父类的同名方法。
示例:
多态性
多态性也是面向对象重要的基本特性之一。“多态”指对象可以表现出多种形态。
继承与多态
在多个子类继承父类,并重写父类方法后,这些子类所创建的对象之间就是多态的。这些对象采用不同的方式实现父类方法。
示例:
鸭子类型测试与多态
Python的多态性更加灵活,支持鸭子类型测试。鸭子类型测试指:若看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟可以被称为鸭子。
由于支持鸭子类型测试,所以Python解释器不检查发生多态的对象是否继承了同一个父类,只要它们有相同的行为(方法),它们之间就是多态的。
例如,我们设计一个函数start( ),它接收具有“叫”speak( )方法的对象,代码如下:
我们定义了几个类,它们都有speak( )方法。
start( )函数可以接收所有speak( )方法对象。
异常处理
为增强程序的健壮性,我们也需要考虑异常处理方面的内容。例如,在读取文件时需要考虑文件不存在、文件格式不正确等异常情况。
第一个异常——除零异常
在数学中,任何整数都不能除以0,如果在计算机程序中将整数除以0,则会引发异常。
示例:
捕获异常
我们不能防止用户输入0,但在出现异常后我们能捕获并处理异常,不至于让程序发生终止并退出。亡羊补牢,为时未晚。
try-except语句
在try代码块中包含在执行过程中可能引发异常的语句,如果没有发生异常,则跳到except代码块执行,这就是异常捕获。
异常捕获是通过try-except语句实现的,基本的try-except语句的语法如下。
try-except语句的执行流程如下。
示例:
从运行的结果可以看出,在输入数字0后,异常发生,跳到except代码块执行。
修改示例:
多个except代码块
多条语句可能会引发多种不同的异常,对每一种异常都会采用不同的处理方式。针对这种情况,我们可以在一个try后面跟多个except代码块,语法如下:
示例:
多重异常捕获
示例:
try-except语句嵌套
示例:
使用finally代码块释放资源
有时在try-except语句中会占用一些资源,例如打开的文件、网络连接、打开的数据库及数据结果集等都会占用计算机资源,需要程序员释放这些资源。为了确保这些资源能够被释放,可以使用finally代码块。
在try-except语句后面还可以跟一个finally代码块,语法如下。
无论是try代码块正常结束还是except代码块异常结束,都会执行finally代码块。
示例:
自定义异常类
示例:
手动引发异常
示例:
常用的内置模块
数学计算模块——math
在math模块中包含数学运算相关的函数等,例如指数、对数、平方根和三角函数等。
本节介绍math模块中的一些常用函数。
示例:
日期时间模块——datetime
Python官方提供的日期和时间模块主要是datetime模块。在datetime模块中提供了如下几个类。
datetime:包含时间和日期。
date:只包含日期。
time:只包含时间。
timedelta:计算时间跨度。
tzinfo:时区信息。
datetime类
datetime类表示日期和时间等信息,我们可以使用如下构造方法创建datetime对象:
示例:
datetime类的常用方法如下。
datetime.today( ):返回当前的本地日期和时间。
datetime.now(tz=None):返回指定时区的当前日期和时间,参数tz用于设置时区,如果参数tz为None或省略,则等同于today( )。
datetime.fromtimestamp(timestamp,tz=None):返回与UNIX时间戳对应的本地日期和时间。UNIX时间戳是从1970年1月1日00:00:00开始到现在为止的总秒数。我们在Python Shell中运行代码,看看运行结果怎样。
示例:
date类
date类表示日期信息,我们可以使用如下构造方法创建date对象:
这些参数的含义和取值范围与datetime类一样
date类的常用方法如下。
date.today( ):返回当前的本地日期。
date.fromtimestamp(timestamp):返回与UNIX时间戳对应的本地日期。
示例:
time类
time类表示一天中的时间信息,我们可以使用如下构造方法创建time对象:
这些参数的含义和取值范围与datetime类一样。
示例:
计算时间跨度类——timedelta
timedelta类的构造方法:
其中的所有参数都可以为整数或浮点数,也可以为正数或负数,如右表所示。
示例:
timedelta可以表示正数或负数时间的间隔,如下代码是等效的。
将日期时间与字符串相互转换
我们经常会遇到将日期时间与字符串相互转换的情况。
1 将日期时间对象转换为字符串时,称之为日期时间格式化。在Python中使用strftime( )方法进行日期时间的格式化,在datetime、date和time三个类中都有一个实例方法strftime(format)。
2 将字符串转换为日期时间对象的过程,叫作日期时间解析。在Python中使用datetime.strptime(date_string,format)类方法进行日期时间解析。
在strftime( )和strptime( )方法中都有一个格式化参数format,用来控制日期时间的格式,常用的日期和时间格式控制符如下表所示。
示例:
正则表达式模块——re
正则表达式指预先定义好一个“字符串模板”,通过这个“字符串模板”可以匹配、查找和替换那些匹配“字符串模板”的字符串。
字符串匹配
字符串匹配指验证一个字符串是否符合指定的“字符串模板”,常用于用户输入验证。例如,用户在注册时要输入邮箱,所以需要验证邮箱是否有效,这就要用到字符串匹配验证。
我们使用match(p,text)函数进行字符串匹配,其中的参数p是正则表达式,即字符串模板,text是要验证的字符串。如果匹配成功,则返回一个Match对象(匹配对象),否则返回None。
示例:
字符串查找
字符串查找指从一个字符串中查找匹配正则表达式的子字符串,常用于数据分析、网络爬虫等数据处理中。
常用的字符串查找函数如下。
search(p,text):在text字符串中查找匹配的内容,如果找到,则返回第1个匹配的Match对象,否则返回None。p是正则表达式。
findall(p,text):在text字符串中查找所有匹配的内容,如果找到,则返回所有匹配的字符串列表;如果一个都没有匹配,则返回None。p是正则表达式。
示例:
字符串替换
正则表达式的字符串替换函数是sub( ),该函数替换匹配的子字符串,返回值是替换之后的字符串,其语法格式如下:
其中,参数pattern是正则表达式;参数repl是用于替换的新字符串;参数string是即将被替换的旧字符串;参数count是要替换的最大数量,默认值为零,表示不限制替换数量。
字符串分割
在Python中使用re模块中的split( )函数进行字符串分割,该函数按照匹配的子字符串进行字符串分割,返回字符串列表对象,其语法格式如下:
其中,参数pattern是正则表达式;参数string是要分割的字符串;参数maxsplit是最大分割次数;maxsplit的默认值为零,表示分割次数没有限制。
文件读写
文件是数据的载体,程序可以从文件中读取数据,也可以将数据写入文件中。
打开文件
我们在使用文件之前要先将文件打开,这通过open( )函数实现。open( )函数的语法如下:
open( )函数中的参数还有很多,这里介绍4个常用参数,这些参数的含义如下。
1. file参数
file参数用于表示要打开的文件,可以是字符串或整数。如果file是字符串,则表示文件名,文件名既可以是当前目录的相对路径,也可以是绝对路径;如果file是整数,则表示一个已经打开的文件。
2. mode参数
mode参数用于设置文件打开模式,用字符串表示,例如rb表示以只读模式打开二进制文件。用于设置文件打开模式的字符串中的每一个字符都表示不同的含义,对这些字符的具体说明如下。
t:以文本文件模式打开文件。
b:以二进制文件模式打开文件。
r:以只读模式打开文件。
w:以只写模式打开文件,不能读内容。如果文件不存在,则创建文件;如果文件存在,则覆盖文件的内容。
x:以独占创建模式打开文件,如果文件不存在,则创建并以写入模式打开;如果文件已存在,则引发FileExistsError异常。
a:以追加模式打开文件,不能读内容。如果文件不存在,则创建文件;如果文件存在,则在文件末尾追加。
+:以更新(读写)模式打开文件,必须与r、w或a组合使用,才能设置文件为读写模式。
这些字符可以进行组合,以表示不同类型的文件的打开模式,如下表所示。
3. encoding参数
encoding用来指定打开文件时的文件编码,默认是UTF-8编码,主要用于打开文本文件。
4. errors参数
errors参数用来指定在文本文件发生编码错误时如何处理。推荐errors参数的取值为'ignore',表示在遇到编码错误时忽略该错误,程序会继续执行,不会退出。
示例:
注:
关闭文件
在打开文件后,如果不再使用该文件,则应该将其关闭,会用到close( )方法。
在finally代码块中关闭文件
对文件的操作往往会抛出异常,为了保证对文件的操作无论是正常结束还异常结束,都能够关闭文件,我们应该将对close( )方法的调用放在异常处理的finally代码块中。
示例:
在with as代码块中关闭文件
with as提供了一个代码块,在as后面声明一个资源变量,在with as代码块结束之后自动释放资源。
示例:
读写文本文件
读写文本文件的相关方法如下。
read(size=-1):从文件中读取字符串,size限制读取的字符数,size=-1指对读取的字符数没有限制。
readline(size=-1):在读取到换行符或文件尾时返回单行字符串。如果已经到文件尾,则返回一个空字符串。size是限制读取的字符数,size=-1表示没有限制。
readlines( ):读取文件数据到一个字符串列表中,每一行数据都是列表的一个元素。
write(s):将字符串s写入文件中,并返回写入的字符数。
writelines(lines):向文件中写入一个字符串列表。不添加行分隔符,因此通常为每一行末尾都提供行分隔符。
flush( ):刷新写缓冲区,在文件没有关闭的情况下将数据写入文件中。
复制文本文件
读写二进制文件
二进制文件的读写单位是字节,不需要考虑编码问题。二进制文件的主要读写方法如下。
read(size=-1):从文件中读取字节,size限制读取的字节数,如果size=-1,则读取全部字节。
readline(size=-1):从文件中读取并返回一行。size是限制读取的行数,如果size=-1,则没有限制。
readlines( ):读取文件数据到一个字节列表中,每一行数据都是列表的一个元素。、
write(b):写入b字节,并返回写入的字节数。
writelines(lines):向文件中写入一个字节列表。不添加行分隔符,因此通常为每一行末尾都提供行分隔符。
flush( ):刷新写缓冲区,在文件没有关闭的情况下将数据写入文件中。
复制二进制文件
注:
图形用户界面
Python中的图形用户界面开发库
注:Qt是一个跨平台的C++应用程序开发框架,被广泛用于开发GUI程序,也可用于开发非GUI程序。
1 TkinterTkinter是Python官方提供的图形用户界面开发库,用于封装Tk GUI工具包,跨平台。但是,Tkinter工具包所包含的控件较少,帮助文档不健全,不便于我们开发复杂的图形用户界面。
2 PyQtPyQt是非Python官方提供的图形用户界面开发库,用于封装Qt工具包,跨平台。若想使用PyQt工具包,则需要额外安装软件包。
3 wxPythonwxPython是非Python官方提供的图形用户界面开发库,也跨平台。它提供了丰富的控件,可用于开发复杂的图形用户界面。它的工具包帮助文档很完善,案例也很丰富。
安装wxPython
在命令提示符(终端)窗口输入pip指令:
在Windows平台上通过pip指令安装wxPython,在命令提示符窗口输入如下指令。
如果安装成功,则可以出现如下窗口。
第一个wxPython程序
图形用户界面主要是由窗口及窗口中的控件构成的,编写wxPython程序其实主要是创建窗口和添加控件的过程。
若要构建一个最简单的wxPython程序,则至少需要一个应用(wx.App)对象和一个窗口(wx.Frame)对象。
通过Python指令在命令提示符窗口中运行文件。
运行并输出结果,弹出如下窗口。
自定义窗口类
在窗口中添加控件
我们在窗口中添加两个控件:一个面板(Panel)和一个静态文本(StaticText)。面板是一个没有标题栏的容器(可以容纳其他控件的控件)。
事件处理
图形界面的控件要响应用户的操作,就必须添加事件处理机制。事件处理的过程如下图所示。
其中涉及的主要内容如下。
1 事件源:事件发生的场所,就是各个控件,例如按钮事件的事件源是按钮。
2 事件:wxPython中的事件被封装为事件类wx.Event及其子类,例如按钮事件类是wx.CommandEvent,鼠标事件类是wx.MoveEvent。
3 事件处理程序:一个响应用户事件的方法。下面通过一个示例介绍事件处理流程。在以下窗口中有一个按钮和一个静态文本,在单击OK按钮时会改变静态文本显示的内容。
下面通过一个示例介绍事件处理流程。在以下窗口中有一个按钮和一个静态文本,在单击OK按钮时会改变静态文本显示的内容。
布局管理
wxPython提供了布局管理器类帮助实现界面布局,主要分为两大类:盒子布局管理器和网格布局管理器。盒子布局类似于CSS中的弹性布局,非常灵活,我们重点介绍盒子布局。
盒子布局管理器
盒子布局管理器类是wx.BoxSizer,Box布局管理器是最常用的布局管理器,它可以让其中的子窗口(或控件)沿垂直或水平方向布局。
1 创建盒子布局管理器对象
我们使用wx.BoxSizer类创建盒子布局管理器对象,主要的构造方法如下:
设置为水平方向布局,
设置为垂直方向布局。
wx.HORIZONTAL是默认值,可以省略
2 添加子窗口(或控件)到父窗口
我们使用wx.BoxSizer对象的Add( )方法添加子窗口(或控件)到父窗口,对Add( )方法的语法说明如下:
添加到父窗口
proportion参数用于设置当前子窗口(或控件)在父窗口中所占的空间比例;flag参数是布局标志,用来控制对齐方式、边框和调整尺寸;border参数用于设置边框的宽度。
下面重点介绍flag标志,flag标志可以分为对齐、边框和调整尺寸。
flag对齐标志如下表所示。
flag边框标志如下表所示。
flag调整尺寸标志如下表所示。
重构事件处理示例
盒子布局管理器嵌套示例
在本例中采用了嵌套布局,首先将两个按钮(b1和b2)放到一个水平方向的盒子布局管理器对象(hbox)中,然后将一个静态文本(statictext)和hbox放到一个垂直方向的盒子布局管理器对象(vbox)中。
控件
wxPython的所有控件都继承自wx.Control类。之前的示例已经使用了静态文本和按钮,本节重点介绍文本输入控件、单选按钮、复选框、列表和静态图片控件。
文本输入控件
文本输入控件(wx.TextCtrl)是可以输入文本的控件。
复选框和单选按钮
多选控件是复选框(wx.CheckBox),复选框(wx.CheckBox)有时也能单独使用,能提供两种状态的开和关。
单选控件是单选按钮(wx.RadioButton),同一组的多个单选按钮应该具有互斥性,就是当一个按钮按下时,其他按钮一定释放。
在界面中实现一组复选框和一组单选按钮。
列表
对列表控件可以进行单选或多选,列表控件类是wx.ListBox。
在界面中实现以下两个列表控件。
wx.LB_SINGLE:单选。
wx.LB_MULTIPLE:多选。
wx.LB_EXTENDED:多选,但是需要在按住Ctrl或Shift键时选择项目。
wx.LB_SORT:对列表选择项进行排序。
静态图片控件
静态图片控件用于显示一张图片,图片可以是wx.Python所支持的任意图片格式,静态图片控件类是wx.StaticBitmap。
在界面中实现两个按钮和一个静态图片控件,在单击按钮时显示不同的图片。
网络通信
基本的网络知识
TCP/IP
在网络通信中会用到一些相关协议,其中,TCP/IP是非常重要的协议,由IP和TCP两个协议构成。IP(Internet Protocol)是一种低级的路由协议,它将数据拆分在许多小的数据包中,并通过网络将它们发送到某一特定地址,但无法保证所有包都抵达目的地,也不能保证包按顺序抵达。
由于通过IP传输数据存在不安全性,所以还需要通过TCP(Transmission Control Protocol,传输控制协议)进行网络通信。TCP是一种高层次的协议,是面向连接的可靠数据传输协议,如果有些数据包没被收到,则会重发,对数据包的内容准确性进行检查并保证数据包按顺序抵达。所以,TCP能够保证数据包安全地按照发送时的顺序送达目的地。
IP地址
为了实现网络中不同计算机之间的通信,每台计算机都必须有一个与众不同的标识,这就是IP地址,TCP/IP使用IP地址来标识源地址和目的地址。
最初,所有的IP地址都是由32位数字构成的,由4个8位的二进制数组成,每8位之间用圆点隔开,例如192.168.1.1,这种类型的地址通过IPv4指定。现在有一种新的地址模式,叫作IPv6,IPv6使用128位数字表示一个地址。尽管IPv6比IPv4有很多优势,但是由于习惯的问题,很多设备还是采用IPv4。
另外,我们有时还会用到一个特殊的IP地址127.0.0.1,127.0.0.1叫作回送地址,指本机。回送地址主要用于网络软件测试及本机的进程间通信,只发送数据,只进行本机进程间通信,不进行任何网络传输。
端口
一个IP地址标识一台计算机,每一台计算机又有很多网络通信程序在运行,提供网络服务或进行通信,这就需要不同的端口进行通信。如果把IP地址比作电话号码,那么端口就是分机号码,在进行网络通信时不仅要指定IP地址,还要指定端口号。
TCP/IP系统中的端口号是一个16位的数字,它的范围是0~65535。将小于1024的端口号保留给预定义的服务,例如HTTP是80,FTP是21,Telnet是23,Email是25,等等。除非要和那些服务进行通信,否则不应该使用小于1024的端口。
HTTP/HTTPS
对互联网的访问大多基于HTTP/HTTPS,HTTP/HTTPS是TCP/IP的一种协议。
1 HTTP
HTTP(Hypertext Transfer Protocol,超文本传输协议)属于应用层协议,其简捷、快速的方式适用于分布式超文本信息传输。HTTP是无连接协议,即在每一次请求时都建立连接,服务器在处理完客户端的请求后,会先应答客户端,然后断开连接,不会一直占用网络资源。
HTTP/1.1共定义了8种请求方法:OPTIONS、HEAD、GET、POST、PUT、DELETE、TRACE和CONNECT。GET和POST方法最常用。
1)GET方法:用于向指定的资源发出请求,被发送的信息“显式”地跟在URL后面。它一般只用于读取数据,例如静态图片等。GET方法有点像使用明信片给别人写信,将“信的内容”写在外面,接触到的人都可以看到,因此是不安全的。
2)POST方法:用于向指定的资源提交数据,请求服务器进行处理,例如提交表单或者上传文件等。数据被包含在请求体中。POST方法像是把“信的内容”装入信封中,接触到该信封的人都看不到信的内容,因此是相对安全的。
2 HTTPS
HTTPS(Hypertext Transfer Protocol Secure,超文本传输安全协议)是超文本传输协议和SSL的组合,用于提供加密通信及对网络服务器身份的鉴定。简单地说,HTTPS是加密的HTTP。
HTTPS与HTTP的区别是:HTTPS使用https://代替http://,HTTPS使用端口443,而HTTP使用端口80与TCP/IP通信。
搭建自己的Web服务器
搭建Web服务器的步骤如下。
1 安装JDK(Java开发工具包)
我们的Web服务器是Apache Tomcat,是支持Java Web技术的Web服务器。Apache Tomcat的运行需要Java运行环境,而JDK提供了Java运行环境,因此我们首先需要安装JDK。
2 配置Java运行环境
Apache Tomcat在运行时需要用到JAVA_HOME环境变量,因此需要先设置JAVA_HOME环境变量。
首先,打开Windows系统环境变量设置对话框,打开该对话框有很多方式,如果是Windows 10系统,则在桌面上用鼠标右键单击“此电脑”图标,弹出Windows系统对话框,之后如下图所示操作。
3 安装Apache Tomcat服务器
我们可以从本章的配套代码中找到Apache Tomcat安装包apache-tomcat-9.0.13.zip,只需将apache-tomcat-9.0.13.zip解压即可安装Apache Tomcat服务器。
4 启动Apache Tomcat服务器
在Apache Tomcat解压目录的bin目录下找到startup.bat文件,双击startup.bat即可启动Apache Tomcat。
启动Apache Tomcat成功后会看到如下信息。
5 测试Apache Tomcat服务器
打开浏览器,在地址栏中输入http://localhost:8080/NoteWebService/,在打开的页面上介绍了当前Web服务器已经安装的Web应用(NoteWebService)的具体使用方法。
打开浏览器,在地址栏中输入网址http://localhost:8080/NoteWebService/note.do,在打开的页面上可以查询所有数据。
urllib.request模块
发送GET请求
如果要发送HTTP/HTTPS的GET请求,则可以使用urllib.request模块的Request对象。
示例:
发送POST请求
如果要发送HTTP/HTTPS的POST请求,则其发送流程与发送GET请求非常类似。
示例:
JSON数据
JSON文档的结构
构成JSON文档的两种结构为:JSON对象(object)和JSON数组(array)。
1 JSON对象
JSON对象类似于Python中的字典类型。
2 JSON数组
JSON数组类似于Python中的列表类型,示例如下:
我们使用json模块提供的loads(str)函数进行JSON数据的解码,参数str是JSON字符串,返回Python数据。
JSON数据的解码
JSON数据的解码(decode)指将JSON数据转换为Python数据,当从网络中接收或从磁盘中读取JSON数据时,需要将其解码为Python数据。
在编码过程中,JSON数据被转换为Python数据。
示例:
下载图片示例
在文件下载成功后,会在当前目录下看到download.png文件。
访问数据库
如果数据量较少,则我们可以将数据保存到文件中;如果数据量较大,则我们可以将数据保存到数据库中。
SQLite数据库
SQLite是嵌入式系统使用的关系数据库,目前的主流版本是SQLite 3。SQLite是开源的,采用C语言编写而成,具有可移植性强、可靠性高、小而易用等特点。SQLite提供了对SQL-92标准的支持,支持多表、索引、事务、视图和触发。
SQLite数据类型
SQLite是无数据类型的数据库,在创建表时不需要为字段指定数据类型。但从编程规范上讲,我们应该指定数据类型,因为数据类型可以表明这个字段的含义,便于我们阅读和理解代码。
SQLite支持的常见数据类型如下。
INTEGER:有符号的整数类型。
REAL:浮点类型。
TEXT:字符串类型,采用UTF-8和UTF-16字符编码。
BLOB:二进制大对象类型,能够存放任意二进制数据。
Python数据类型与SQLite数据类型的映射
在使用Python访问SQLite数据库时,会经常涉及数据类型的互相转换。它们的映射关系如下表所示。
使用GUI管理工具管理SQLite数据库
1 安装和启动DB Browser for SQLite
从本章配套代码中找到DB.Browser.for.SQLite-3.11.2-win32.zip安装包文件,将该文件解压到一个目录中,在解压目录下找到DB Browserfor SQLite.exe文件,双击该文件即可启动DB Browser for SQLite工具。
2 创建数据库
一个SQLite数据库对应一个SQLite数据文件,为了测试DB Browserfor SQLite工具,我们要先创建SQLite数据库。
在上图所示的界面单击工具栏中的“新建数据库”按钮,弹出保存文件对话框。
3 创建数据表
在一个SQLite数据库中可以包含多个数据表。在上图所示的界面单击“保存”按钮,弹出建表对话框。
4 执行SQL语句
使用DB Browser for SQLite工具,可以执行任意合法的SQL语句。
5 浏览数据
DB Browser for SQLite常用于浏览数据。
数据库编程的基本操作过程
数据库编程主要分为两类:查询(Read)和修改(C插入、U更新、D删除)。
1 查询数据
查询数据时需要6步,在查询过程中需要提取数据结果集,最后释放资源,即关闭游标和数据库。
2 修改数据
修改数据时如上图所示,最多需要6步,在修改过程中如果执行SQL操作成功,则提交数据库事务;如果失败,则回滚事务。最后释放资源,关闭游标和数据库。
sqlite3模块API
Python官方提供了sqlite3模块来访问SQLite数据库。
数据库连接对象Connection
数据库访问的第一步是进行数据库连接。
我们可以通过connect(database)函数建立数据库连接,参数database是SQLite数据库的文件路径,如果连接成功,则返回数据库连接对象Connection。
Connection对象有如下重要的方法。
close( ):关闭数据库连接,在关闭之后再使用数据库连接将引发异常。
commit( ):提交数据库事务。
rollback( ):回滚数据库事务。
cursor( ):获得Cursor游标对象。
游标对象Cursor
一个Cursor游标对象表示一个数据库游标,游标暂时保存了SQL操作所影响到的数据。游标是通过数据库连接创建的。
游标Cursor对象有很多方法和属性,其中的基本SQL操作方法如下。
execute(sql[,parameters]):执行一条SQL语句,sql是SQL语句,parameters是为SQL提供的参数,可以是序列或字典类型。返回值是整数,表示执行SQL语句影响的行数。
executemany(sql[,seq_of_params]):执行批量SQL语句,sql是SQL语句,seq_of_params是为SQL提供的参数,seq_of_params是序列。返回值是整数,表示执行SQL语句影响的行数。
在通过execute( )和executemany( )方法执行SQL查询语句后,还要通过提取方法从查询结果集中返回数据,相关提取方法如下。
fetchone( ):从结果集中返回只有一条记录的序列,如果没有数据,则返回None。
fetchmany(size=cursor.arraysize):从结果集中返回小于等于size记录数的序列,如果没有数据,则返回空序列,size在默认情况下是整个游标的行数。
fetchall( ):从结果集中返回所有数据。
数据库的CRUD操作示例
对数据库表中的数据可以进行4类操作:数据插入(Create)、数据查询(Read)、数据更新(Update)和数据删除(Delete),即增、删、改、查。
示例中的数据表
为了查询方便,我们预先插入几条记录。
在输入SQL语句后,单击该按钮执行所有SQL语句
无条件查询
SQL查询语句是SELECT,根据是否带有WHERE子句,分为:无条件查询和有条件查询,本节先介绍无条件查询。
无条件查询最为简单,没有WHERE子句。
示例:
有条件查询
有条件查询带有WHERE子句,WHERE子句是查询条件。
插入数据
数据插入操作SQL语句是INSERT。
示例:
数据插入成功,可以使用DB Browser for SQLite浏览数据。
更新数据
数据更新操作SQL语句是UPDATE。
示例:
数据更新成功,可以使用DB Browser for SQLite浏览数据。
删除数据
数据删除操作SQL语句是DELETE。
示例:
数据更新成功,可以使用DB Browser for SQLite浏览数据。
删除数据
数据删除操作SQL语句是DELETE。
示例:
数据更新成功,可以使用DB Browser for SQLite浏览数据。
多线程
如果想让我们的程序同时执行多个任务,就需要使用多线程技术了。
线程相关的知识
进程
一个进程就是一个正在执行的程序,每一个进程都有自己独立的一块内存空间、一组系统资源。在进程的概念中,每一个进程的内部数据和状态都是完全独立的。
在Windows操作系统中,一个进程就是一个exe或者dll程序,它们相互独立,相互也可以通信。
线程
在一个进程中可以包含多个线程,多个线程共享一块内存空间和一组系统资源。所以,系统在各个线程之间切换时,开销要比进程小得多,正因如此,线程被称为轻量级进程。
主线程
Python程序至少有一个线程,这就是主线程,程序在启动后由Python解释器负责创建主线程,在程序结束后由Python解释器负责停止主线程。
在多线程中,主线程负责其他线程的启动、挂起、停止等操作。其他线程被称为子线程。
线程模块——threading
Python官方提供的threading模块可以进行多线程编程。threading模块提供了多线程编程的高级API,使用起来比较简单。
在threading模块中提供了线程类Thread,还提供了很多线程相关的函数,这些函数中常用的如下。
active_count( ):返回当前处于活动状态的线程个数。
current_thread( ):返回当前的Thread对象。
main_thread( ):返回主线程对象。主线程是Python解释器启动的线程。
示例:
创建子线程
创建一个可执行的子线程,需要如下两个要素。
1 线程对象:线程对象是threading模块的线程类Thread或Thread子类所创建的对象。
2 线程体:线程体是子线程要执行的代码,这些代码会被封装到一个函数中。子线程在启动后会执行线程体。实现线程体主要有以下两种方式。
1)自定义函数实现线程体。
2)自定义线程类实现线程体。
自定义函数实现线程体
创建线程Thread对象的构造方法如下:
target参数指向线程体函数,我们可以自定义该线程体函数;通过name参数可以设置线程名,如果省略这个参数,则系统会为其分配一个名称;args是为线程体函数提供的参数,是一个元组类型。
示例:
自定义线程类实现线程体
另外一种实现线程体的方式是,创建一个Thread子类并重写run( )方法,run( )方法就是线程体函数。
示例:
线程管理
线程管理包括线程创建、线程启动、线程休眠、等待线程结束和线程停止,其中,线程创建、线程启动和线程休眠在16.3节已经用到了,这些不再赘述。本节重点介绍等待线程结束和线程停止的内容。
等待线程结束
有时,一个线程(假设是主线程)需要等待另外一个线程(假设是t1子线程)执行结束才能继续执行。
join( )方法的语法如下:
参数timeout用于设置超时时间,单位是秒。如果没有设置timeout,则可以一直等待,直到结束。
示例:
从运行结果来看,在子线程t1结束后,主线程才输出变量value的内容,这说明主线程被阻塞了。
如果尝试将t1.join( )语句注释掉,则输出结果如下:
从运行结果可见,子线程t1还没有结束,主线程就输出变量value的内容了。
线程停止
在线程体结束时,线程就停止了。但在某些业务比较复杂时,会在线程体中执行一个“死循环”。线程体是否持续执行“死循环”是通过判断停止变量实现的,“死循环”结束则线程体结束,线程也就结束了。
另外,在一般情况下,死循环会执行线程任务,然后休眠,再执行,再休眠,直到结束循环。
示例:
下载图片示例
这个网络爬虫程序每隔一段时间都会执行一次下载图片任务,在下载任务完成后,休眠一段时间再执行。这样反复执行,直到爬虫程序停止。
示例: