Python系列教程
第1章 Python 语言概述
Python编程规范+最佳实践
python文件:.py,.ipynb, pyi, pyc, pyd, pyo都是什么文件?
第1章必背的内容
第2章 Python基础
字符串切片
Python数据类型大战:可变与不可变,谁主沉浮?
Python「布尔类型」:不只是True和False!
Python「枚举类型」:你真的了解枚举类型吗?
Python字符串格式化:哪种字符串格式化方法最快?
Python字符串编码:为什么你的网页显示乱码?
Python「内置变量」:不只是变量,更是编程的魔法!
Python变量的作用域,你真的了解吗?
Python中如何使用F-Strings格式化浮点数
第2章必备的内容
第3章 流程控制和异常处理
加速Python for循环
for循环
Python三元表达式:让代码简洁与效率提升成为可能
Python「While循环」:何时打破循环?
Python「异常处理」:程序出错了?不慌
这样可以减少IF语句的使用,从此告别if-else噩梦
Python循环加速的秘方,可提速上千倍!
如何在Python中优雅地使用断言?这篇文章给你答案!
Python异常处理:12个异常处理技巧,你掌握了几个?
Python中的and和or你真的会用吗,代码逻辑竟然可以如此简单!
Python的else子句7个妙用,原来还能这样用,整挺好!
快来看!Python写代码,没有pass怎么行?
第三章 必背的内容
第4章 高级数据结构
Python字典嵌套:编程界的俄罗斯套娃
Python「类型注解」:如何让你的Python代码够清晰?
列表越界了?来学学Python列表的花式操作!
Python字典的这些黑科技,你用过几个?
Python元组:为何你的代码需要这个'不变'的伙伴?
试试Python具名元组NamedTuple吧!用过的都说好
Python中的5种队列数据结构,你知道几个?
itertools模块让你的代码原地起飞!
第5章 正则表达式
正则表达式
本文档使用 MrDoc 发布
-
+
首页
第3章 流程控制和异常处理
  流程控制指的是代码运行逻辑、分支走向、循环控制,是真正体现程序执行顺序的操作。流程控制一般分为顺序执行、条件选择执行和循环执行三种。   `Python`程序中的语句默认都是按照代码编写顺序自上而下的依次执行,执行过程中脚本之间没有进行逻辑判断,只使用顺序执行是绝对不够的。因为在使用脚本完成日常生活中的各项任务时,需要在特定条件下将某些语句重复执行或者设定指定条件后再执行,这就是本章要着重讲解的选择结构和循环结构。 # 学习目标 - 掌握单分支、双分支、多分支结构的使用方法 - 掌握`for循环`和`while循环`的使用方法 - 了解`break`和`continue`的使用方法和应用场景 - 掌握异常捕获的流程 - 了解异常的分类 # 3.1 选择结构设计   选择结构顾名思义就是通过某些特定条件来决定下一步应该执行哪些操作。例如日常生活中通过相亲来评判对方是否能成为你心目中的白马王子,通常在见面之后会有很丰富的心理过程:“如果ta很有钱,我可以考虑,否则我不想搭理ta”,“如果他身高180,我很满意,如果身高175,比较满意,要不然说再见”。上述描述中出现的“如果”、“否则”、“要不然”等词语实际上就是编程中的选择结构。   `Python`中的选择结构包括多种:`单分支结构`、`双分支结构`、`多分支结构`和`分支结构嵌套`等。 ## 3.1.1 if单分支结构   `如果`翻译为英文就是`if`,通常表示对一个条件是否成立的一个假设。“如果我考了100分,爸爸就会带我出去玩”,上述场景就是`单分支结构`。`单分支`,表示只考虑一种情况,其它情况忽略不计,其对应的语法如下。 ```python if 条件表达式: 如果条件成立,则执行这块代码 ```   提示:`if`后的条件表达式之后应该以`英文的冒号`结尾;代码块前面应该有间隔,这个间隔称之为`缩进`。   【示例3-1】定义一个变量`score`表示分数并设为`100`,如果`score`大于`70`,就输出`爸爸带我出去玩`。 ```python score = 100 if score > 70: # 如果分数超过70分 print('爸爸带我出去玩') ```   运行结果: ```python 爸爸带我出去玩 ```   上述代码中的`score>70`就是条件表达式,而`print(xxx)`就是代码块。其中`100`确实大于`70`,所以条件成立并直接执行代码块的内容输出相应内容。   代码中`>`两边应该加上空格,否则会看到下方出现灰色波浪线,意为`PEP 8: E225 missing whitespace around operator`,提示符号两端缺少了空格,如图3-1所示。这不叫语法错误,而是编码规范警告,不强制要求,但最好遵守。提示中的`PEP8`是`Python`开发者必须遵守的`编码规范`,它会告诉你代码怎么写会更整洁,使代码具有更高的可读性。    【示例3-2】定义一个变量`score`表示分数并设为`65`,如果`score`大于等于`60`并且小于`70`,就输出`你的成绩良好`。 ```python score = 65 if score >= 60 and score < 70: # 成绩大于等于60 并且 小于70 print('你的成绩良好') ```   运行结果: ```python 你的成绩良好 ```   上述示例中使用了`and`,表示`并且`,含义为`条件1`成立并且`条件2`也成立,整个条件表达式才算成立。题目中分数为`65`,不仅满足`>=60`,也满足`<70`,所以条件成立。如果有一个不满足或两个都不满足会怎样?   【示例3-3】定义一个变量`score`表示分数并设为`80`,如果`score`大于等于`60`并且小于`70`,就输出`你的成绩良好`。 ```python score = 80 if score >= 60 and score < 70: print('你的成绩良好') ```   运行结果为空!虽然`80>=60`但是80不小于70,两个条件只有一个成立所以条件整体不成立。再次强调,`and`的特点就是两个条件都成立整个条件才算成立。   【示例3-4】定义两个变量,`temperature`表示你的体温,`symptom`表示你的症状,如果温度超过`37`并且症状为`咳嗽`,则输出`你可能阳了`。 ```python temperature = 38.5 symptom = '咳嗽' # 如果体温超过37度并且症状为咳嗽 if temperature > 37 and symptom = '咳嗽': print('你可能阳了') ```   运行结果: ```python if temperature > 37 and symptom = '咳嗽': ^ SyntaxError: invalid syntax ```   运行结果显示抛出异常且异常类型为`SyntaxError`。上述条件语句的代码中`=`是`赋值号`,并不能判断相等操作,正确做法应该是使用`==`才是真正意义的比较相等的符号。   【示例3-5】使用`==`比较两个字符串是否相等。 ```python temperature = 38.5 symptom = '咳嗽' if temperature > 37 and symptom == '咳嗽': print('你可能阳了') ```   运行结果 ```python 你可能阳了 ```   结论:当判断数字、字符串是否和另一个值相等时需使用`==`符号。   【示例3-6】定义两个变量,`money`表示金钱并设置为`100万`,`age`表示年龄并设为`50`。如果年龄小于`30`或者金钱超过`50`万,就输出`相亲成功`。 ```python money = 1000000 age = 50 if age < 30 or money > 500000: # 如果年龄小于30岁或者存款超过50万 print('相亲成功') ```   运行结果 ```python 相亲成功 ```   上述代码使用了`or`关键字,表示为`或者`。意为两边的条件有一个成立则整体条件就成立。 ## 3.1.2 `if-else`双分支结构   有单分支结构肯定就有双分支结构。前面的`if`语句是单分支结构,如果判断条件成立则执行代码块内容,否则就跳过。该结构只考虑了一种可能性,执行或者不执行。   而双分支结构考虑的是如果执行成功做什么,执行失败又做什么,提供了选择的权力。 双分支结构的语法格式如下。 ```python if 条件表达式: 如果条件成立,则执行这块代码 else: 如果条件不成立,则执行这块代码 ```   上述语法中出现了`else`,翻译为`否则`的意思。双分支结构的特点是不管判断条件是何结果,总是要从两个代码块中选择一个代码块来执行。   【示例3-7】从终端输入一个用户名,如果为`root`则提示`超级管理员登录`,如果不是则提示`普通用户登录`。 ```python username = input('请输入登录的用户名:') if username == 'root': # 如果用户名为root print('超级管理员登录') else: # 如果用户名不是root print('普通用户登录') ```   运行结果 ```python 请输入登录的用户名:root 超级管理员登录 请输入登录的用户名:zhangsan 普通用户登录 ``` ### 提示 - if和else后都要以冒号结尾,表示条件语句结束; - if和else中的代码块左边都要加缩进。   【示例3-8】从终端输入一个用户名,再输入一个密码。如果用户名为`root`且密码为`123456`则提示`登录成功`,否则提示`用户名或密码错误`。 ```python username = input('请输入登录的用户名:') password = input('请输入登录的密码:') # 如果用户名为root并且密码为123456 if username == 'root' and password == '123456': print('登录成功') else: print('用户名或密码错误') ```   运行结果 ```python 请输入登录的用户名:root 请输入登录的密码:123456 登录成功 请输入登录的用户名:zhangsan 请输入登录的密码:123456 用户名或密码错误 ```   运行后的输出语句符合脚本逻辑的预期。 ## 3.1.3 `if-elif-else`多分支结构   `多分支结构`,就是使用`if-else`语句把一件事考虑的更全面的写法,例如教师需要根据你的成绩评判你的等级标准,如果大于等于`90`分则属于优秀,如果位于`70-90`分则属于良好,`60-70`分属于及格,`60`分以下则属于不及格。你考了`75`分,应该属于哪个等级?   上面的例子中将一个人的成绩分为了四种情况来考虑,情况分的越多则考虑的越全面。   多分支结构的语法如下: ```python if 条件表达式1: 如果条件1成立,则执行这块代码 elif 条件表达式2: 如果条件2成立,则执行这块代码 …… else: 否则执行这块代码 ```   其中上述结构中出现的`elif`关键字其实是`elif if`的缩写,可以根据实际情况动态添加。   【示例3-9】从终端输入成绩用`score`变量表示,如果大于等于`90`则输出`优秀`,位于`70-90`之间则输出`良好`,`60-70`则输出`及格`,`60`以下则输出`不及格`。 ```python score = input("请输入你的成绩:") if score >= 90: print('优秀') elif score < 90 and score >= 70: print('良好') elif score < 70 and score >= 60: print('及格') else: print('不及格') ```   运行结果 ```python 请输入你的成绩:90 Traceback (most recent call last): if score >= 90: TypeError: '>=' not supported between instances of 'str' and 'int' ```   运行结果直接抛出异常,异常类型为`TypeError`,意思是字符串和整型之间不支持使用`>=`进行比较。产生问题的原因很简单,因为`input`接收的数据不管是什么内容,返回的永远都是字符串类型,而字符串不能跟整型比较大小,所以可以通过`类型转换`来解决。 ```python score = input("请输入你的成绩:") score = int(score) if score >= 90: …… ```   运行结果 ```python 请输入你的成绩:90 优秀 请输入你的成绩:50 不及格 ```   上述示例中使用的`int()`表示将字符串类型的数字转换为整型数字,从运行结果来看,输入的两个成绩都可以正常显示对应的等级。如果录入`zhangsan`作为学生成绩会怎样?直接抛出异常,错误原因如下: ```python 请输入你的成绩:zhangsan Traceback (most recent call last): score = int(score) ValueError: could not convert string to int: 'zhangsan' ```   异常类型为`ValueError`,表示数值错误,含义为`不能将string类型的zhangsan转换为int类型`。更完善的写法如下所示。 ```python score = input("请输入你的成绩:") if score.isdigit(): # 判断输入的成绩是否由纯数字组成 score = int(score) if score >= 90: …… else: print('输入数据格式不对') ```   运行结果 ```python 请输入你的成绩:zhangsan 输入数据格式不对 ```   上述代码中使用`isdigit()`方法判断输入的内容是否为纯数字,如果是纯数字则考虑等级评定的结果,否则直接提示格式不对。   显然,上述代码在编辑器中会出现灰色波浪线,鼠标放到波浪线处,提示如图3-2所示。    提示中的内容为`脚本有更简单的写法`,点击`Simplify chained comparison`选项后会自动将语法进行纠正。将`score < 90 and score >= 70`纠正为`90 > score >= 70`。   思考:上述示例中的`90 > score >= 70`可以简化为`score >= 70`,`70 > score >= 60`可以简化为`score >= 60`,为什么? ## 3.1.4 分支结构嵌套   将一个`if语句`放到另一个`if语句`中就形成了分支结构嵌套。   有时候,人们需要处理的事物逻辑十分复杂,需要综合判断很多种因素,在不同因素下还有其他条件会影响结果,这个时候就可以通过分支结构嵌套来解决。   【示例3-10】判断你的相亲对象是否符合结婚标准。   假设你是一名女性,现在要见一名网友并且有和ta处对象的打算,在见面前你的心理活动应该如图3-3所示:    由上图可知,整个相亲过程很复杂,要考虑性别、外貌、车、房、存款等因素,用编程的方式实现如下。 ```python print('你好,我是你的网友') sex = input('对方的性别是(填男/女):') if sex == '男': face = input('对方帅不帅(填帅/不帅):') if face == '帅': print('一见钟情') else: money = input('对方的存款有多少(填金额):') money = int(money) # 将金额转为整型方便后续的判断 if money > 1000000: print('土豪我们做朋友吧') else: house = input('有没有房子(填有/没有)') if house == '有': print('有你的地方就是家') else: car = input('有没有车(填有/没有)') if car == '有': print('你是个潜力股,我看好你') else: print('少年还需努力') else: print('我们适合做闺蜜') ```   运行结果 ```python 你好,我是你的网友 对方的性别是(填男/女):男 对方帅不帅(填帅/不帅):不帅 对方的存款有多少(填金额):500000 有没有房子(填有/没有)没有 有没有车(填有/没有)有 你是个潜力股,我看好你 ```   上述示例中只是使用了`if-else`嵌套,语法并没有难度,但是给人的感觉却很复杂。因为不仅要考虑各种各样的判断条件,还需要考虑代码缩进,嵌套越多则代码越复杂,这种写法就属于过度嵌套。通常情况下推荐嵌套`最多写2层`,也就是`if`里面最多套用一个`if`。   【示例3-11】使用函数解决`过度嵌套`问题。 ```python def meetfriend(): print('你好,我是你的网友') sex = input('对方的性别是(填男/女):') if sex != '男': print('我们适合做闺蜜') return face = input('对方帅不帅(填帅/不帅):') if face == '帅': print('一见钟情') return money = input('对方的存款有多少(填金额):') money = int(money) if money > 1000000: print('土豪我们做朋友吧') return house = input('有没有房子(填有/没有)') if house == '有': print('有你的地方就是家') return car = input('有没有车(填有/没有)') if car == '有': print('你是个潜力股,我看好你') else: print('少年还需努力') meetfriend() ```   运行结果跟优化前的示例一样。本示例引入了函数的概念(在第6章会讲到),通过将条件取反并配合`return`语句,将复杂的`if嵌套`变成了单分支结构,使代码难度降低。 # 3.2 循环结构设计   `Python`中的循环结构主要包括`for`、`while`两种结构。 ## 3.2.1 for循环结构   `for循环`结构的语法格式如下。 ```python for 迭代变量 in 可迭代对象: 需重复执行的代码片段 ```   什么是可迭代对象?`Python`中的可迭代对象一般包括字符串、列表、元组、字典、集合、`range`等。   什么是迭代变量?从可迭代对象中取出的数据就是可迭代变量。例如,从一堆数字里面取出的都是单独的数字、从一堆学生中取出的都是单独的学生。可迭代对象在本章比较常用的是`range`函数,其格式如下: ```python range(stop) # 默认start=0,step=1 range(start,stop) # 默认step=1 range(start,stop,step) ```   【示例3-12】使用`for`循环配合`range`生成3以内的数字,并打印。 ```python for i in range(3): print('范围内容为', i) ```   运行结果 ```python 范围内容为 0 范围内容为 1 范围内容为 2 ```   上述示例中使用了`range`函数。`range`是Python的内置函数,表示生成一个范围。此处只填了一个参数为3,表示生成一个3以内的范围,也就是0、1、2。   【示例3-13】使用`for循环`批量评定学生成绩,从终端输入3个学生成绩,判定其为`优/良/及格/不及格`。 ```python for i in range(3): score = int(input('请输入学生成绩:')) if score >= 90: print(score, '优秀') elif score >= 70: print(score, '良好') elif score >= 60: print(score, '及格') else: print(score, '不及格') ```   运行结果 ```python 请输入学生成绩:60 60 及格 请输入学生成绩:100 100 优秀 请输入学生成绩:80 80 良好 ```   【示例3-14】从终端输入`数字n`,并使用`for循环`计算出`n以内`的和并打印结果。 ```python n = int(input('你想计算多少以内的和:')) total = 0 for i in range(n+1): # range(n+1)的范围是0,1,…n total = total + i # 累计求和 print(n, '以内的和为', total) ```   运行结果 ```python 你想计算多少以内的和:100 100 以内的和为 5050 ```   提示:`range(n)`表示的含义是生成`0~n-1`范围内的数字,也可以表示为`[0,n)`。   【示例3-15】从终端输入两个数字,`m`表示开始数字假设为3,`n`表示结束数字假设为10,计算两者之间所有数字的和。 ```python m = int(input('你的开始数字:')) n = int(input('你的结束数字:')) total = 0 for i in range(m, n+1): total = total + i print(f'{m}~{n}以内的和为', total) ```   运行结果 ```python 你的开始数字:3 你的结束数字:10 3~10以内的和为 52 ```   通过上述脚本计算`3~10`之间的和为`52`,结果正确。如果用户输入的m大于等于n该怎么办?可以使用`变量交换`来实现。   【示例3-16】交换用户输入的`m`和`n`的值。 ```python m = int(input('你的开始数字:')) n = int(input('你的结束数字:')) if m > n: m, n = n, m # 变量m和n进行交换 total = 0 for i in range(m, n+1): total = total + i print(f'{m}~{n}以内的和为', total) ```   运行结果 ```python 你的开始数字:10 你的结束数字:3 3~10以内的和为 52 ```   上述示例中通过`m,n=n,m`快速完成了变量的交换,这样简洁的语法其它编程语言并不具备。   【示例3-17】计算`m到n`之间的和,要求每隔4个数字计算1次。例如输入2和10,则应该计算2、6、10三个数字的和。 ```python start = int(input('你的开始数字:')) end = int(input('你的结束数字:')) step = int(input('你的步长:')) if start > end: start, end = end, start total = 0 for i in range(start, end+1, step): total = total + i print(f'{start}~{end}以内步长为{step}的和为', total) ```   运行结果 ```python 你的开始数字:2 你的结束数字:10 你的步长:4 2~10以内步长为4的和为 18 ```   由上述代码可知,`range`的第三个参数`step`表示为`步长`,意为从起始数字开始每次加一个步长,如果不设置步长则默认为1。该数值可以为正数,也可以为负数。 ## 3.2.2 while循环结构   while翻译为中文表示`当……时`,它是Python的第二种循环机制,其语法格式如下。 ```python while 条件表达式: 如果条件成立,则重复执行这块代码 ```   `while循环`的特点是根据循环条件来决定具体循环次数。   【示例3-18】使用`while循环`计算`n以内`的和。 ```python n = int(input('你想计算多少以内的和:')) i = 0 # 表示循环次数,默认为0 total = 0 while i <= n: total = total + i i = i + 1 # 每循环一次就加1 print(n, '以内的和为', total) ```   运行结果 ```python 你想计算多少以内的和:100 100 以内的和为 5050 ```   上述代码中的`i=i+1`相当于`range`中的`step`设为1,`i<=n`相当于`range(i,n+1)`。   【示例3-19】计算`m到n`之间的和,要求每隔4个数字计算1次。例如输入2和10,则应该计算2、6、10三个数字的和。 ```python start = int(input('你的开始数字:')) end = int(input('你的结束数字:')) step = int(input('你的步长:')) if start > end: # 如果起始数字大于结束数字,则两者进行交换 start, end = end, start total = 0 x = start while x <= end: total = total + x x = x + step print(f'{start}~{end}以内步长为{step}的和为', total) ```   运行结果 ```python 你的开始数字:2 你的结束数字:10 你的步长:4 2~10以内步长为4的和为 18 ```   思考:`while循环`和`for循环`功能几乎一样,为什么还要学习两种循环结构?通过表3-1可以有所了解。 表3-1 两种循环结构对比 | 循环结构名称 | 功能性 | 循环次数 | 代码量 | 场景 | | --- | --- | --- | --- | --- | | for循环 | 两者等价 | 已知 | 简洁 | 适合遍历 | | while循环 | 两者等价 | 未知 | 略复杂 | 适合带条件的循环 |   由上表可知,`for循环`更适合遍历,`while循环`更适合在多条件下循环,两者从功能上来讲相同,但是`for循环`更方便。 ## 3.2.3 循环嵌套   日常生活中有些事物使用一个循环是无法实现的,需要借助两个甚至更多的循环才能实现,而一个循环中使用另一个循环就是循环嵌套。   【示例3-20】使用循环嵌套打印九九乘法表。 ```python for i in range(1, 10): for j in range(1, i + 1): print(f"{j}*{i}={i*j} ", end='') print() ```   运行结果 ```python 1*1=1 1*2=2 2*2=4 1*3=3 2*3=6 3*3=9 1*4=4 2*4=8 3*4=12 4*4=16 1*5=5 2*5=10 3*5=15 4*5=20 5*5=25 1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36 1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49 1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64 1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81 ```   实现思路:第一层循环表示的含义是第i行,第二层循环表示的是第j列,通过行和列进行求和。   提示:每计算完一行才应该换行,否则应该输出在一行之中。   【示例3-21】使用循环打印金字塔,效果如图3-4所示:  图3-4 金字塔效果图 ```python max_level = int(input('请输入金字塔最大层数:')) for current_level in range(1, max_level + 1): for i in range(max_level - current_level): print(' ', end='') for j in range(2 * current_level - 1): print('*', end='') print() ```   运行结果 ```python 请输入金字塔最大层数:5 * *** ***** ******* ********* ```   实现思路:第一个循环表示金字塔的行,第二个循环表示每一行应该加几个空格,第三个循环表示每一行应该加几个`*`,每输出一行就换行。   【示例3-22】利用字符串的特点简化金字塔。 ```python max_level = int(input('请输入金字塔最大层数:')) for current_level in range(1, max_level + 1): print(' '*(max_level - current_level), end='') print('*'*(2 * current_level - 1), end='') print() ```   改进之处在于每创建一行,只需要生成相应长度的空格和`*`即可,借助字符串的乘法操作快速完成这个需求,优化后的代码更为简单明了。   提示:循环嵌套要慎重使用。因为嵌套的循环层次越深,意味着最里面的代码循环执行的次数会成几何式增长。假设单层循环次数为5次,那么嵌套三层循环就意味着最里面的代码会执行`5^3=125`次,会大大降低代码的执行效率,应避免使用循环嵌套,或更换编程思路。 # 3.3 循环跳转   在`Python`中,跳转语句是一种特殊类型的语句,它们的作用是允许程序在执行过程当中跳过某些代码块。比较常见的跳转语句包括`break`、`continue`、`return`、`yield`等。本小节将重点讲解`break`和`continue`,因为它们是循环搭配的跳转语句,其它关键字在后面的章节会进行讲解。 ## 3.3.1 break语句   `break`翻译为“打破、断绝”,在循环代码中,它的作用是提前结束循环,并跳出循环体。   【示例3-23】计算用户输入的数字的和直到输入非数字为止。   请实现一个循环,允许用户从终端输入数字,每输入完成一个就自动提示输入下一个,直到输入非数字为止。请计算用户输入的所有数字的和,效果如图3-5所示:  图3-5 运行效果图 ```python inno = '' total = 0 while True: inno = input('请输入用来求和的数字:') if inno.isdigit(): total += int(inno) else: break # 如果输入的不是数字就退出 print(f"求和的结果为:{total}") ```   由题目可知,因为不确定要循环几次,所以选择`while循环`,由于不清楚循环条件,所以将条件设为`True`,表示死循环。如果用户输入的信息不是数字类型就通过`break`跳出。   `死循环`:永远不会结束的循环就称为死循环,也称为无限循环。一般程序中不会轻易使用,因为它极其占用系统资源,所以都需要通过`break`语句跳出循环,否则当资源耗尽就会“死机”。   【示例3-24】计算`m以内`的平方值小于`n`的所有数字。   从终端输入一个`数字m`表示`m以内`的所有数字,从终端输入一个`数字n`表示平方的最大值。请计算`m以内`每个数字的平方`小于n`的数字,效果如图3-6所示:  ```python m = int(input('请输入一个数字:')) n = int(input('请输入平方最大值:')) for i in range(1, m+1): if i*i > n: # 如果i的平方大于n就跳出 break print(f"{i}*{i}={i*i}") ```   运行结果 ```python 请输入一个数字:5 请输入平方最大值:10 1*1=1 2*2=4 3*3=9 ``` ## 3.3.2 continue语句   `continue`的含义和`break`类似,翻译为 “继续”。在循环中表示的含义为跳出本次循环,继续执行下一次循环。   【示例3-25】计算`m以内`是`n`整数倍的数字。   从终端输入一个`数字m`表示`m以内`的数字,从终端输入一个`数字n`表示倍数。请计算`m以内`的数字中哪些数字是`n`的倍数并打印是几倍,效果如图3-7所示:  ```python m = int(input('请输入一个数字:')) n = int(input('请输入倍数:')) for i in range(1, m + 1): if i % n != 0: # 如果i除以n的余数为0 continue print(f"{i}是{n}的{i//n}倍") ```   运行结果 ```python 请输入一个数字:10 请输入倍数:3 3是3的1倍 6是3的2倍 9是3的3倍 ```   上述代码中,`continue`的作用是如果发现条件不成立则直接跳过后续的数据语句直接进入下一次循环。代码中使用了`i//n`表示整除,结果为整数,如果写成`i/n`那么结果为浮点数,不符合要求。   【示例3-26】统计薪资大于`3000`的所有人的平局工资。   从终端输入员工信息,若薪资不是整型则重新输入,否则统计工资大于等于`3000`的员工数量并计算所有人的平局工资,输入过程中随时按q退出,效果如图3-8所示:  ```python count = total = 0 while True: salary = input('请输入员工的工资(按q|Q退出):') if salary.upper() == 'Q': # 如果输入的内容转为大写等于Q break if not salary.isdigit(): # 如果输入的内容不是数字 continue salary = int(salary) if salary >= 3000: total += salary # 薪资累计求和 count += 1 print(f'工资超过3000的人累计{count}个,平均薪资{total//count}元') ```   运行结果 ```python 请输入员工的工资(按q|Q退出):3000 请输入员工的工资(按q|Q退出):4000 请输入员工的工资(按q|Q退出):6000 请输入员工的工资(按q|Q退出):1000 请输入员工的工资(按q|Q退出):q 工资超过3000的人累计3个,平均薪资4333元 ```   提示:不管是`break`还是`continue`语句,必须在`for`或者`while`循环内部进行使用,无法单独使用。 ## 3.3.3 else语句   循环结构和选择结构类似,也有`else`语句,其语法格式如下。 ```python for 迭代变量 in 可迭代对象: 需重复执行的代码片段 else: 循环正常结束后要执行的代码 ``` 和 ```python while 条件表达式: 如果条件成立,则重复执行这块代码 else: 循环正常结束后要执行的代码 ```   由表3-1可知,`while循环`和`for循环`功能上基本等价,本节以`for循环`结构为例来说明`for-else`的用法。   【示例3-27】计算`n以内`的所有质数并打印。   质数就是指在大于1的自然数中,除了1和它本身以外无法被其它自然数整除的数,例如`2、3、5、7、11`等就是质数,也称为素数,效果如图3-9所示:  ```python m = int(input('请输入获取几以内的质数:')) i = 2 for i in range(2, m + 1): # 质数从2开始,因此range起始值为2 j = 2 for j in range(2, i): if i % j == 0: # 如果i是质数 break else: print(f"{i}是质数") ```   运行结果 ```python 请输入获取几以内的质数:15 2是质数 3是质数 5是质数 7是质数 11是质数 13是质数 ```   提示:`else`中的代码也属于循环的一部分,一旦跳出就不会被执行。 # 3.4 异常处理   异常翻译为`exception`,一般是指一个事件,该事件会在程序执行过程中人为或者意外发生,会直接影响程序的正常执行甚至会导致整个程序强制中断退出,俗称“报错”。本节将从异常的分类、异常的排查和异常捕获三个方面进行说明。 ## 3.4.1 Python中异常的分类   编码过程中常见的一种类型错误,如图3-10所示。    在运行程序的过程中,`xxxError`意味着程序发生了异常,先通过表3-2了解一下异常的分类。 表3-2 常见的异常分类 | 异常名称 | 解释 | | --- | --- | | AssertionError | 断言语句失败 | | AttributeError | 对象没有这个属性 | | BaseException | 所有异常的基类 | | Exception | 常规错误的基类 | | ImportError | 导入模块/对象失败 | | IndentationError | 缩进错误 | | IndexError | 序列中没有此索引(index) | | IOError | 输入/输出操作失败 | | KeyboardInterrupt | 用户中断执行(通常是输入ctrl+C) | | KeyError | 映射中没有这个键 | | MemoryError | 内存溢出错误(对于Python 解释器不是致命的) | | NameError | 未声明/初始化对象 (没有属性) | | NotImplementedError | 尚未实现的方法 | | OSError | 操作系统错误 | | OverflowError | 数值运算超出最大限制 | | ReferenceError | 弱引用(Weak reference)试图访问已经垃圾回收了的对象 | | RuntimeError | 一般的运行时错误 | | RuntimeWarning | 可疑的运行时行为(runtime behavior)的警告 | | StandardError | 所有的内建标准异常的基类 | | StopIteration | 迭代器没有更多的值 | | SyntaxError | Python 语法错误 | | TypeError | 不同类型数据之间的无效操作 | | UnboundLocalError | 访问未初始化的本地变量 | | UnicodeDecodeError | Unicode 解码时的错误 | | UnicodeEncodeError | Unicode 编码时错误 | | UnicodeError | Unicode 相关的错误 | | UnicodeTranslateError | Unicode 转换时错误 | | UserWarning | 用户代码生成的警告 | | ValueError | 传入无效的参数 | | ZeroDivisionError | 除(或取模)为零 (所有数据类型) |   由上图可知,`TypeError`对应的解释为不同类型数据之间的无效操作,理解为“因为数据类型不一致导致某些语法不能使用”。在抛出异常时一般会明确标记代码出错的行号,如图3-11所示。左边的`main.py`表示出错的脚本,`line 3`表示第三行有错,下面显示的`for i in range(2, m + 1)`表示报错的代码,报错的地方就在这一句代码附近。程序源码如图3-12所示。  图3-11 TypeError异常  图3-12 源码图  图3-13 语法提示图   由图3-12可知,第3行的代码中“1”被一个黄色背景选中,鼠标放上去后会弹出一个提示,如图3-13所示,提示信息为“期望得到一个`str`,但是得到了`int`”。由编程经验可知,`input`返回的数据是字符串类型,而字符串不能和数字相加减,故异常原因就是`m`的类型和整型类型不一致导致无法相加减,解决方案就是对`m`强制类型转换,在第1行后新增一句`m = int(m)`即可。   【示例3-28】计算`m`除以`n`并打印结果。   从终端输入两个数字,`m`表示被除数,`n`表示除数,请计算`m/n`的值并保留两位小数。 ```python m = int(input('请输入被除数:')) n = int(input('请输入除数:')) result = round(m/n, 2) # round保留m/2的结果为2为小数 print(f"{m}/{n}={result}") ```   运行结果 ```python 请输入被除数:10 请输入除数:5 10/5=2.0 ```   上述脚本可以计算`10/5`的值,计算`10/0`的结果如下所示: ```python 请输入被除数:10 请输入除数:0 Traceback (most recent call last): File "D:/main.py", line 3, in <module> result = round(m/n, 2) ZeroDivisionError: division by zero ```   由表3-2可知,该异常为`ZeroDivisionError`,查表为`除(或取模)为零`,意思是进行除法运算或者取模运算时除数为0,显示为第3行报错,通过分析代码应该是`n`导致的。而`n`是通过第2行由终端输入的,所以报错原因是用户在终端输入除数的时候为0导致抛出了该异常。需要使用`if`分支结构对`n`进行判断。如果`n`为0则提示输入错误,否则进行正常运算。 ## 3.4.2 捕获异常 当我们已知脚本会抛出具体异常的时候,可以借助`try-except`语句进行异常捕获,语法格式如下。 ```python try: 可能有问题的代码 except 异常类型1 as e: 出现异常类型1的代码 except 异常类型2 as e: 出现异常类型2的代码 …… except 异常类型n as e: 出现异常类型n的代码 ``` try语句的工作原理如下:   (1)先执行`try`子句(`try`和`exception`之间的语句);   (2)如果`try`子句执行完毕都没有触发异常则跳过所有`except`子句并继续向下运行;   (3)如果`try`子句执行过程中发生异常则直接跳过剩余未执行的代码部分,将异常类型与`except`关键字后要捕获的异常进行匹配,若匹配成功则执行`except`子句,匹配失败则继续向下匹配,若还未匹配成功则抛出代码异常并中断整个程序。   【示例3-29】计算`m`除以`n`并打印结果(设置`n`为0)。   请编写代码用于计算两个数的和并保留两位小数,要求从终端循环输入数据,计算完成一组后自动计算下一组,直到输入`q`退出。 ```python while True: m = input('请输入被除数:') n = input('请输入除数:') # 如果m和n中有一个为Q则退出 if m.upper() == 'Q' or n.upper() == 'Q': print('退出') break result = round(int(m)/int(n), 2) print(f"{m}/{n}={result}") ```   运行结果 ```python 请输入被除数:10 请输入除数:2 10/2=5.0 请输入被除数:4 请输入除数:0 Traceback (most recent call last): File "D:/main.py", line 8, in <module> result = round(int(m)/int(n), 2) ZeroDivisionError: division by zero ```   由运行结果可知,代码中存在异常错误且已知异常原因为`ZeroDivisionError`,出错后导致整个脚本退出,无法继续进行除法运算,可以考虑使用`try-except`协助编程。   【示例3-30】计算`m除以n`并打印结果(使用异常捕获拦截潜在的异常) ```python while True: m = input('请输入被除数:') n = input('请输入除数:') if m.upper() == 'Q' or n.upper() == 'Q': print('退出') break try: result = round(int(m)/int(n), 2) print(f"{m}/{n}={result}") except ZeroDivisionError as e: print('除数为0,没关系,继续执行') ```   运行结果 ```python 请输入被除数:10 请输入除数:2 10/2=5.0 请输入被除数:3 请输入除数:0 除数为0,没关系,继续执行 请输入被除数: ```   由运行结果可知,当除数输入为0的时候不再报错,仅提示`除数为0,没关系,继续执行`,然后直接跳过这次异常进入下一次的运行流程。   【示例3-31】通过身高和体重计算`BMI`指数。   通过终端输入身高和体重,然后通过`BMI = 体重/身高^2`公式计算`BMI`,然后根据条件判定身体状态。`BMI<16.5`为极瘦,`BMI<18.5`属于偏瘦,`BMI<24`属于正常,`BMI<28`属于超重,大于等于`28`属于肥胖。 ```python while True: h = int(input('请输入您的身高(米):')) w = int(input('请输入您的体重(公斤):')) BMI = w / h ** 2 # 根据身高和体重计算BMI参数 if BMI < 16.5: state = '极瘦' elif BMI < 18.5: state = '偏瘦' elif BMI < 24.0: state = '正常' elif BMI < 28.0: state = '超重' else: state = '肥胖' print(f'您的体重指数为:{state}') ```   运行结果 ```python 请输入您的身高(米):1 请输入您的体重(公斤):20 您的体重指数为:正常 请输入您的身高(米):1.8 Traceback (most recent call last): File "D:/main.py", line 2, in <module> h = int(input('请输入您的身高(米):')) ValueError: invalid literal for int() with base 10: '1.8' 请输入您的身高(米):0 请输入您的体重(公斤):20 Traceback (most recent call last): File "D:/main.py", line 4, in <module> BMI = w / h ** 2 ZeroDivisionError: division by zero ```   由运行结果可知,当身高是小数的时候抛出`ValueError`异常,当身高是0的时候抛出`ZeroDivisionError`异常,上述出现的异常都导致程序中断退出。   【示例3-32】计算BMI指数并捕获多个已知异常。 ```python while True: try: h = int(input('请输入您的身高(米):')) w = int(input('请输入您的体重(公斤):')) BMI = w / h ** 2 if BMI < 16.5: state = '极瘦' elif BMI < 18.5: state = '偏瘦' elif BMI < 24.0: state = '正常' elif BMI < 28.0: state = '超重' else: state = '肥胖' print(f'您的体重指数为:{state}') except ValueError as e: print('ValueError异常,先继续执行') except ZeroDivisionError as e: print('ZeroDivisionError异常,先继续执行') ```   上述代码中使用`except`关键字捕获了两个异常类型,分别是`ValueError`和`ZeroDivisionError`,整个程序暂时可以正常运行。   【示例3-33】计算`BMI`指数并捕获`Exception`异常。 ```python while True: try: h = int(input('请输入您的身高(米):')) w = int(input('请输入您的体重(公斤):')) BMI = w / h ** 2 if BMI < 16.5: state = '极瘦' elif BMI < 18.5: state = '偏瘦' elif BMI < 24.0: state = '正常' elif BMI < 28.0: state = '超重' else: state = '肥胖' print(f'您的体重指数为:{state}') except Exception as e: print('不管抛出什么异常,继续向下执行') ```   运行结果 ```python 请输入您的身高(米):1.8 不管抛出什么异常,继续向下执行 请输入您的身高(米):0 请输入您的体重(公斤): ```   上述示例中对`Exception`异常进行了捕获,相当于对所有异常都进行了捕获。因为内异常类型都`继承于`Exception这个“类”。   提示:本节讲解的异常捕获是一种投机取巧的行为,通过`try-except`语句可以让程序暂时不会因为异常中断,但是没有从根本解决程序的报错问题,就像一艘漏水的船,通过不断向外舀水可以让船不至于沉下去,但它其实还在漏水。如果将来某一天水越漏越多,那么船迟早会沉。 # 3.5 示例:实现猜拳游戏   本示例指人类和电脑之间进行猜拳游戏,由电脑随机选择石头、剪刀、布,再由用户自己做出选择,最后得出胜负的一个过程。本示例主要使用到的技术包括`while`循环、`if-elif-else`分支语句、输入输出语句、随机函数等。 ## 3.5.1 电脑随机猜拳   `random`模块为随机模块,可以生成随机数。假设1为石头,2为剪刀,3为布,则代码可以写为: ```python import random # randint:随机生成1-3的随机整数 # 1石头 2剪刀 3布 computer = random.randint(1, 3) if computer == 1: print('电脑出石头') elif computer == 2: print('电脑出剪刀') else: print('电脑出布') ``` ## 3.5.2 用户进行猜拳   用户从终端输入猜拳信息,以数字的形式来表示,同样用`1、2、3`来表示,对应的信息和电脑的猜拳信息保持一致。   提示:用户输入的内容可能是`1、2、3`,也可能是`a、b、c`,或者是其它内容,需要通过`if`进行格式校验,代码如下: ```python player = input('请输入猜拳数据(1石头、2剪刀、3布、0退出):') if player.isdigit(): player = int(player) if player == 1: print('用户出石头') elif player == 2: print('用户出剪刀') elif player == 3: print('用户出布') elif player == 0: print('用户选择退出') else: print('指令错误') else: print('指令错误') ``` ## 3.5.3 电脑和用户判断胜负   猜拳的胜负大概分为九种情况,分别根据胜负、平手等情况进行比较,胜负情况如图3-14所示:  图3-14 猜拳流程图   按照上图绘制的流程图,针对各种情况进行判断胜负的操作,代码如下:   【示例3-34】猜拳游戏完整代码。 ```python import random # randint:随机生成1-3的随机整数 # 1石头 2剪刀 3布 computer = random.randint(1, 3) player = input('请输入猜拳数据(1石头、2剪刀、3布、0退出):') if player.isdigit(): player = int(player) if player == 1: print('用户出石头') if computer == 1: print('电脑出石头') print('平手') elif computer == 2: print('电脑出剪刀') print('用户胜利') else: print('电脑出布') print('电脑胜利') elif player == 2: print('用户出剪刀') if computer == 1: print('电脑出石头') print('电脑胜利') elif computer == 2: print('电脑出剪刀') print('平手') else: print('电脑出布') print('用户胜利') elif player == 3: print('用户出布') if computer == 1: print('电脑出石头') print('用户胜利') elif computer == 2: print('电脑出剪刀') print('电脑胜利') else: print('电脑出布') print('平手') elif player == 0: print('用户选择退出') else: print('指令错误') else: print('指令错误') ``` ## 3.5.4 简化代码   示例有9种判断胜负的情况,再加上用户输入的一些情况判断,导致整体代码出现了很多的分支结构嵌套,使用函数和条件取反优化,也可以通过改变代码逻辑来简化。本示例选择优化代码逻辑。   通过观察发现,9种情况中有3种属于平局,3种用户胜利,3种电脑胜利,所以条件可以简化为下面的代码:   【示例3-35】猜拳游戏简化版代码。 ```python import random while True: computer = random.randint(1, 3) player = input('请输入猜拳数据(1石头、2剪刀、3布、0退出):') if player.isdigit(): player = int(player) if player == computer: print('平手') continue if player == 1 and computer == 2 or player == 2 and computer == 3 or player == 3 and computer == 1: print('用户胜利') continue if player == 1 and computer == 3 or player == 2 and computer == 1 or player == 3 and computer == 2: print('电脑胜利') continue print('指令错误') else: print('指令错误') ```   优化后的脚本将判断胜负的条件进行了合并,而且利用了`continue`关键字去掉了`else`的情况,整体代码得到了简化,也丧失了一定的猜拳过程,比如现在只知道猜拳结果,不知道用户和电脑猜拳的内容。这体现了一个细节:代码的简洁性和功能性不能两者兼得,要在两者之间进行取舍做好平衡。 # 3.6 本章小结   本章主要学习了`Python`基础中十分重要的选择结构和循环结构,也就是`if`、`if-else`、`if-elif-else`语句,还有`for`、`while`两种循环,最后讲解了常见的异常处理,列举了常见的异常错误、如何定位并调试代码错误、如何捕获异常错误等。 # 3.7 思考练习 ## 3.7.1 单选题 1.现在需要循环执行某段代码100次,则推荐使用? A.for循环 B.while循环 C.if单分支 D.if双分支 2.循环过程中可以使用哪个关键字跳出整个循环并继续向下执行? A.exit B.continue C.range D.break 3.range(1,5) 表示生成的范围是? A.(1,5) B.[1,5) C.[1,5] D.以上都对 4.异常处理过程中经常看到line xxx,则表示错误位于? A.xxx行 B.xxx上面 C.xxx下面 D.xxx周围 5.IndexError表示的异常原因是? A.索引越界 B.除数为0 C.类型不对 D.语法不对 ## 3.7.2 填空题 1.执行循环语句`for x in range(1,5,2):print(x)`,循环体执行的次数是_____(填数字)。 2.条件A并且条件B同时成立中的并且在代码中表示为_____(填小写字母)。 3.捕获异常使用的是_____和_____两个关键字(填小写字母)。 4.跳出本次循环继续下次循环可以使用关键字_____(填小写字母)。 5.循环语句`for i in range(6,-4,-2):`会循环执行_____ 次,循环变量`i`的最终值应当为____(填数字)。 ## 3.7.3 编程题 1.编写脚本实现猜数字游戏。大致流程是电脑随机生成一个1-100的数字,由用户进行猜,如果猜对了则结束游戏,如果猜小了或猜大了则进行提示并继续游戏,直到猜对为止。 2.编写脚本,由用户在终端输入一个在1到15之间的整数,然后显示一个金字塔,运行效果如图3-15所示。  图3-15 金字塔效果图 3.编写脚本,由用户在终端输入一个在1到15之间的整数,然后显示一个三角形,运行效果如图3-16所示。  图3-16 三角形 4.编写脚本,由用户在终端输入一个数字,然后使用循环输出下图3-17所示的图形。  图3-17 金字塔 5.编写脚本,由用户在终端输入一个数字,然后使用循环输出下图3-18所示的图形。  图3-18 菱形
张泽楠
2024年6月25日 10:39
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码