扫一扫 视频讲解 第3章〓程序流程控制 扫一扫 知识图谱 在Python程序中,对于语句的执行有三种基本的控制结构,即顺序结构、选择结构、循环结构。另外,当程序出错时,Python使用异常处理流程进行处理(具体请参见第7章)。 3.1程序的流程 3.1.1输入、处理和输出 无论程序的规模如何,每个程序都可以分为以下三部分: 程序通过输入接收待处理的数据(Input); 然后执行相应的处理(Process); 最 图31程序的输入、处理和输出示意图 后通过输出(Output)返回处理的结果。该过程通常称为IPO程序编写方法。其示意图如图31所示。 (1) 输入数据。输入是一个程序的开始。程序要处理的数据有多种来源,形成了多种输入方式,包括交互输入、参数输入、随机数据输入、文件输入、网络输入等。 (2) 处理数据。处理是程序对输入数据进行计算产生输出结果的过程。计算问题的处理方法统称为“算法”。 (3) 输出结果。输出是程序输出结果的方式。程序的输出方式包括控制台输出、图形输出、文件输出、网络输出等。 【例3.1】计算球体表面积和体积的程序的IPO描述。 输入(I):输入r(球体的半径) 处理(P):计算球体的表面积s = 4 * math.pi * r * r 计算球体的体积v = 4 * math.pi * r * r * r / 3 输出(O):输出s和v 3.1.2算法和数据结构 程序还可以使用以下公式描述: 程序=算法+数据结构 算法是执行特定任务的方法。数据结构是一种存储数据的方式,有助于求解特定的问题。算法通常与数据结构紧密相关。算法可以描述为“建立一个特定的数据结构,然后采用某种方式使用该数据结构”。 描述算法的最简单方法是使用自然语言描述。对于较复杂的算法,为了描述其细节,往往采用伪代码进行描述。伪代码是一种类似于程序设计语言的文本,其目的是为读者提供在代码中实现算法所需的结构和细节,而无须将算法局限于特定的程序设计语言。 【例3.2】求解两个整数最大公约数的算法的自然语言描述。 求解两个整数的最大公约数(Great Common Divisor,GCD)的一种算法是辗转相除法,又称欧几里得算法。辗转相除法算法的自然语言描述如下。 (1) 对于已知的两个正整数m和n,使得m>n。 (2) m除以n得到余数r。 (3) 若r≠0,则令m←n,n←r,重复步骤(2),继续m除以n得到新的余数r。若仍然r≠0,则重复此过程,直到r=0为止。最后的m就是最大公约数。 【例3.3】求解两个整数最大公约数的辗转相除算法的伪代码描述。 // 求解m和n的最大公约数。GCD(m, n) = GCD(n, m Mod n) GCD(m, n) While (n != 0) remainder = m Mod n// 计算余数 m = n n = remainder End While Return m End GCD 说明: 伪代码没有特定对应的语言规则。本书采用本例中类似Python缩进规则的伪代码描述。 【例3.4】求解两个整数最大公约数的辗转相除算法的Python代码实现(gcdTest.py)。 #求解m和n的最大公约数。GCD(m, n) = GCD(n, m Mod n) def Mygcd(m,n): #定义函数 if (m < n): m, n = n, m #m和n的值交换 while (n != 0): m, n = n, m%n return m if __name__ == '__main__': #如果独立运行时,则运行测试代码 print(24,36,"的最大公约数为:",Mygcd(24,36)) 图32两个整数的最大公约数 程序运行结果如图32所示。 3.1.3程序流程图 程序流程图(Flow Chart)又称为程序框图,是描述程序运行具体步骤的图形表示。通过标准符号详细描述程序的输入、处理和输出过程,可以作为程序设计的最基本依据。 流程图的基本元素主要包括以下几种。 (1) 开始框和结束框()。表示程序的开始和结束。 (2) 输入框/输出框()。表示输入和输出数据。 (3) 处理框()。表示要执行的流程或处理。 (4) 判断框()。表示条件判断,根据判断的结果执行不同的分支。 图33计算所输入数据a的平方根的流程图 (5) 箭头线()。表示程序或算法的走向。 【例3.5】使用程序流程图(如图33所示)描述计算所输入数据a的平方根的程序。 常用的程序结构包括顺序结构、选择结构和循环结构。本章将陆续展开阐述。 3.2顺序结构 若程序中的语句按各语句出现位置的先后次序执行,称之为顺序结构,参见图34。在图34中先执行语句块1,再执行语句块2,最后执行语句块3,三个语句块之间是顺序执行关系。 【例3.6】顺序结构示例(area.py): 输入三角形三条边的边长(为简单起见,假设这三条边可以构成三角形),利用海伦公式计算三角形的面积。提示: 计算三角形面积的海伦公式为s=h*(h-a)*(h-b)*(h-c),其中,a、b、c是三角形三边的边长,h是三角形周长的一半。 import math #导入模块 a = float(input("请输入三角形的边长a:")) #提示输入边长a,并转换为浮点数 b = float(input("请输入三角形的边长b:")) #提示输入边长b,并转换为浮点数 c = float(input("请输入三角形的边长c:")) #提示输入边长c,并转换为浮点数 h = (a + b + c) / 2 #三角形周长的一半 area = math.sqrt(h*(h-a)*(h-b)*(h-c)) #三角形面积 print(str.format("三角形三边分别为:a={0},b={1},c={2}", a, b, c)) print(str.format("三角形的面积 = {0}", area)) #输出三角形的面积 程序运行结果如图35所示。 图34顺序结构示意图 图35顺序结构求三角形面积的运行结果 3.3选择结构 选择结构可以根据条件来控制代码的执行分支,也称为分支结构。Python使用if语句来实现分支结构。 3.3.1分支结构的形式 分支结构包含单分支、双分支和多分支等多种形式,流程如图36(a)~(c)所示。 图36if语句的选择结构 3.3.2单分支结构 if语句单分支结构的语法形式如下。 if (条件表达式): 语句/语句块 其中: (1) 条件表达式: 可以是关系表达式、逻辑表达式、算术表达式等。 (2) 语句/语句块: 可以是单个语句,也可以是多个语句。多个语句的缩进必须一致。 当条件表达式的值为真(True)时,执行if后的语句(块),否则不做任何操作,控制将转到if语句的结束点。其流程如图36(a)所示。 条件表达式可以是任意表达式,其最后评价结果为bool值True(真)或False(假)。如果表达式的结果为数值类型(0)、空字符串("")、空元组(())、空列表([])、空字典({}),其bool值为False(假); 否则其bool值为True(真)。例如,123、"abc"、(1,2)均为True。 【例3.7】单分支结构示例(if_2desc.py): 输入两个整数a和b,比较两者大小,使得a大于b。 a = int(input("请输入第1个整数:")) #输入整数1 b = int(input("请输入第2个整数:")) #输入整数2 print(str.format("输入值:{0}, {1}", a, b)) #显示所输入的两个整数 if (a < b): #如果a小于b a, b = b, a #a和b交换 print(str.format("降序值:{0}, {1}", a, b)) #输出降序排序结果 图37单分支结构比较两数大小 程序运行结果如图37所示。 3.3.3双分支结构 if语句双分支结构的语法形式如下。 if (条件表达式): 语句/语句块1 else: 语句/语句块2 当条件表达式的值为真(True)时,执行if后的语句(块)1,否则执行else后的语句(块)2,其流程如图36(b)所示。 Python提供了下列条件表达式来实现等价于其他语言的三元条件运算符((条件)?语句1: 语句2)的功能: 条件为真时的值if(条件表达式) else 条件为假时的值 例如,如果x≥0,则y=x,否则y=0,可以表述为: y = x if (x >=0) else 0 【例3.8】计算分段函数: y= sinx+2x+e4-(x+1)3x≥0ln(-5x)-|x2-8x|7x+ex<0 此分段函数有以下几种实现方式,请读者自行编程测试。 (1) 利用单分支结构实现。 if (x>=0): y = math.sin(x) + 2 * math.sqrt(x + math.exp(4)) - math.pow(x + 1, 3) if (x<0): y = math.log(-5 * x) - math.fabs(x * x - 8 * x) / (7 * x) + math.e (2) 利用双分支结构实现。 if (x>=0): y = math.sin(x) + 2 * math.sqrt(x + math.exp(4)) - math.pow(x + 1, 3) else: y = math.log(-5 * x) - math.fabs(x * x - 8 * x) / (7 * x) + math.e (3) 利用条件运算语句实现。 y = (math.sin(x) + 2 * math.sqrt(x + math.exp(4)) - math.pow(x + 1, 3)) if ((x>=0)) else \ (math.log(-5 * x) - math.fabs(x * x - 8 * x) / (7 * x) + math.e) 3.3.4多分支结构 if语句多分支结构的语法形式如下。 if (条件表达式1) : 语句/语句块1 elif (条件表达式2) : 语句/语句块2 … elif (条件表达式n) : 语句/语句块n [else: 语句/语句块n+1;] 该语句的作用是根据不同条件表达式的值确定执行哪个语句(块),其流程如图36(c)所示。 【例3.9】已知某课程的百分制分数mark,将其转换为五级制(优、良、中、及格、不及格)的评定等级grade。评定条件如下: 成绩等级=优mark≥90良80≤mark<90中70≤mark<80及格60≤mark<70不及格mark<60 根据评定条件,有以下4种不同的方法实现。 方法一: mark = int(input("请输入分数: ")) if (mark >= 90): grade = "优" elif (mark >= 80): grade = "良" elif (mark >= 70): grade = "中" elif (mark >= 60): grade = "及格" else: grade = "不及格" 方法二: mark = int(input("请输入分数: ")) if (mark >= 90): grade = "优" elif (mark >= 80 and mark < 90): grade = "良" elif (mark >= 70 and mark < 80): grade = "中" elif (mark >= 60 and mark < 70): grade = "及格" else: grade = "不及格" 方法三: mark = int(input("请输入分数: ")) if (mark >= 90): grade = "优" elif (80<= mark < 90): grade = "良" elif (70 <= mark < 80): grade = "中" elif (60 <= mark < 70): grade = "及格" else: grade = "不及格" 方法四: mark = int(input("请输入分数: ")) if (mark >= 60): grade = "及格" elif (mark >= 70): grade = "中" elif (mark >= 80): grade = "良" elif (mark >= 90): grade = "优" else: grade = "不及格" 其中,方法一中使用关系运算符“>=”,按分数从大到小依次比较; 方法二和方法三使用关系运算符和逻辑运算符表达完整的条件,即使语句顺序不按比较的分数从大到小依次书写,也可以得到正确的等级评定结果; 方法四使用关系运算符“>=”,但按分数从小到大依次比较。 上述四种方法中,方法一、方法二和方法三正确,而且方法一最简捷明了,方法二和方法三虽然正确,但是存在冗余条件。方法四虽然语法没有错误,但是判断结果错误: 根据mark分数所得等级评定结果只有“及格”和“不及格”两种,请读者根据程序流程自行分析原因。 【例3.10】已知坐标点(x,y),判断其所在的象限(if_coordinate.py)。 x = int(input("请输入x坐标:")) y = int(input("请输入y坐标:")) if (x == 0 and y == 0): print("位于原点") elif (x == 0): print("位于y轴") elif (y == 0): print("位于x轴") elif (x > 0 and y > 0): print("位于第一象限") elif (x < 0 and y > 0): print("位于第二象限") elif (x < 0 and y < 0): print("位于第三象限") else: print("位于第四象限") 3.3.5if语句的嵌套 在if语句中又包含一个或多个if语句称为if语句的嵌套。一般形式如下。 if (条件表达式1): if (条件表达式11): 语句1 [else: 语句2]内嵌if [else: if (条件表达式21): 语句3 [else: 语句4]]内嵌if 【例3.11】计算分段函数: y=1x>00x=0-1x<0 此分段函数有以下几种实现方式,请读者判断哪些是正确的?并自行编程测试正确的实现方式。 方法一(多分支结构): if (x > 0): y = 1 elif (x == 0): y = 0 else: y = -1 方法二(if语句嵌套结构): if (x >= 0): if (x > 0): y = 1 else: y = 0 else: y = -1 方法三: y = 1 if (x != 0): if (x < 0): y = -1 else: y = 0 方法四: y = 1 if (x != 0): if (x < 0): y = -1 else: y = 0 请读者画出每种方法相应的流程图,并进行分析测试。其中,方法一、方法二和方法三是正确的,而方法四是错误的。 3.3.6if语句典型示例代码 if语句的典型示例代码如表31所示。当if或else的语句块仅包含一条语句时,该语句也可以直接写在关键字if或者else语句的同一行后面,以实现紧凑代码。 表31if语句的典型示例代码 程 序 功 能代 码 片 段 求绝对值 if a < 0: a = -a a和b按升序排序 if a > b: a, b = b, a 求a和b的最大值 if a > b: maximum = a else:maximum = b 计算两个数相除的余数,如果除数为0,则给出报错信息 if b == 0: print("除数为0") else: print("余数为: " + a % b) 计算并输出一元二次方程的两个根。如果判别式b2-4ac<0,则显示“方程无实根”的提示信息 delta = b*b - 4.0*a*c if delta < 0.0: print("方程无实根") else: d = math.sqrt(delta) print((-b + d)/(2.0*a)) print((-b - d)/(2.0*a)) 3.3.7选择结构综合举例 【例3.12】输入三个数,按从大到小的顺序排序(if_3desc.py)。 先比较a和b,使得a>b; 然后比较a和c,使得a>c,此时a最大; 最后b和c比较,使得b>c。 a = int(input("请输入整数a:")) b = int(input("请输入整数b:")) c = int(input("请输入整数c:")) if (a < b): a, b=b, a #a和b交换,使得a>b if (a < c): a, c=c, a #a和c交换,使得a>c if (b < c): b, c=c, b #b和c交换,使得b>c print("排序结果(降序):", a, b, c) 程序运行结果如图38所示。 【例3.13】编程判断某一年是否为闰年(leapyear.py)。判断闰年的条件是年份能被4整除但不能被100整除,或者能被400整除,其判断流程参见图39所示。 图38三个数降序排序结果 图39闰年的判断流程 方法一: 使用一个逻辑表达式包含所有的闰年条件,相关语句如下。 if ((y % 4 == 0 and y % 100 != 0) or y % 400 == 0): print("是闰年") else: print("不是闰年") 方法二: 使用嵌套的if语句,相关语句如下。 if (y % 400 == 0): print("是闰年") else: if (y % 4 == 0): if (y % 100 == 0): print("不是闰年") else: print("是闰年") else: print("不是闰年") 方法三: 使用ifelif语句,相关语句如下。 if (y % 400 == 0): print("是闰年") elif (y % 4 != 0): print("不是闰年") elif (y % 100 == 0): print("不是闰年") else: print("是闰年") 方法四: 使用calendar模块的isleap()函数来判断闰年,相关语句如下。 if (calendar.isleap(y)): print("是闰年") else: print("不是闰年") 3.4循环结构 循环结构用来重复执行一条或多条语句。使用循环结构可以减少源程序重复书写的工作量。许多算法需要使用到循环结构。Python使用for语句和while语句来实现循环结构。 3.4.1可迭代对象 可迭代对象(iterable)一次返回一个元素,因而适用于循环。Python包括以下几种可迭代对象: 序列(sequence),例如字符串(str)、列表(list)、元组(tuple)等; 字典(dict); 文件对象; 迭代器对象(iterator); 生成器函数(generator)。 迭代器是一个对象,表示可迭代的数据集合,包括方法__iter__()和__next__(),可以实现迭代功能。 生成器是一个函数,使用yield语句,每次产生一个值,也可以用于循环迭代。 有关可迭代对象的详细信息请参见本书9.8节内容。 3.4.2range对象 在Python 3中,内置对象range是一个迭代器对象,迭代时产生指定范围的数字序列,其格式如下。 range(start, stop[, step]) range返回的数值序列从start开始,到stop结束(不包含stop)。如果指定了可选的步长step,则序列按步长step增长。例如: >>> for i in range(1,11): print(i, end=' ') #输出:1 2 3 4 5 6 7 8 9 10 >>> for i in range(1,11,3): print(i, end=' ') #输出:1 4 7 10 >>> list(range(5, 0, -1)) #输出:[5, 4, 3, 2, 1] 注意: Python 2中range的类型为函数,是一个生成器; Python 3中range的类型为类,是一个迭代器。 3.4.3for循环 for语句用于遍历可迭代对象集合中的元素,并对集合中的每个元素执行一次相关的嵌入语句。当集合中的所有元素完成迭代后,控制传递给for之后的下一个语句。for语句的格式如下。 for 变量 in 可迭代对象集合: 循环体语句/语句块 例如: >>> for i in (1,2,3): print(i, i**2, i**3) 1 1 1 2 4 8 3 9 27 【例3.14】利用for循环求1~100中所有奇数的和以及偶数的和(for_sum1_100.py)。 sum_odd = 0; sum_even = 0 #循环赋初值 for i in range(1, 101): #i=1~100 if i % 2 != 0: #奇数 sum_odd += i #奇数和 else: #偶数 sum_even += i #偶数和 print("1~100中所有奇数的和:", sum_odd) print("1~100中所有偶数的和:", sum_even) 图310for循环求和的运行结果 程序运行结果如图310所示。 3.4.4while循环 与for循环一样,while也是一个预测试的循环,但是while在循环开始前并不知道重复执行循环语句序列的次数。while语句按不同条件执行循环语句(块)零次或多次。while循环语句的格式如下。 while (条件表达式): 循环体语句/语句块 图311while循环的执行流程 while循环的执行流程如图311所示。 说明: (1) while循环语句的执行过程如下。 ① 计算条件表达式。 ② 如果条件表达式结果为True,控制将转到循环语句(块),即进入循环体。当到达循环语句序列的结束点时转①,即控制转到while语句的开始,继续循环。 ③ 如果条件表达式结果为False,退出while循环,即控制转到while循环语句的后继语句。 (2) 条件表达式是每次进入循环之前进行判断的条件,可以为关系表达式或逻辑表达式,其运算结果为True(真)或False(假)。条件表达式中必须包含控制循环的变量。 (3) 循环语句序列可以是一条语句,也可以是多条语句。 (4) 循环语句序列中至少应包含改变循环条件的语句,以使循环趋于结束,避免“死循环”。 【例3.15】利用while循环求∑100i=1i,以及1~100中所有奇数的和、偶数的和(while_sum.py)。 i = 1; sum_all = 0; sum_odd = 0; sum_even = 0 while (i <= 100): sum_all += i #所有数之和 if (i % 2 == 0): #偶数 sum_even += i #偶数和 else: #奇数 sum_odd += i #奇数和 i += 1 #改变循环条件 print("和=%d、奇数和=%d、偶数和=%d" % (sum_all, sum_odd, sum_even)) 程序运行结果如图312所示。 【例3.16】用以下近似公式求自然对数的底数e的值,直到最后一项的绝对值小于10-6为止(while_e.py)。 e≈1+11!+12!+…+1n! i = 1; e = 1; t = 1 while (1/t >= pow(10,-6)): t *= i e += 1 / t i += 1 print("e =", e) 程序运行结果如图313所示。 图312while循环求和 图313while循环求自然对数 3.4.5循环的嵌套 若在一个循环体内又包含另一个完整的循环结构,则称之为循环的嵌套。这种语句结构称为多重循环结构。内层循环中还可以包含新的循环,以形成多层循环结构。 在多层循环结构中两种循环语句(for循环、while循环)可以相互嵌套。多重循环的循环次数等于每一重循环次数的乘积。 【例3.17】利用嵌套循环打印运行效果如图314所示的九九乘法表(nest_for.py)。 for i in range(1, 10): #外循环 s = "" for j in range(1, 10): #内循环 s += str.format("{0:1}*{1:1}={2:<2} ", i, j, i * j) print(s) 图314九九乘法表运行效果图 思考: 请修改程序,分别打印如图315(a)和图315(b)所示的九九乘法表。 图315九九乘法表的另外两种显示效果 3.4.6break语句 break语句用于退出for循环、while循环,即提前结束循环,接着执行循环语句的后继语句。注意,当多个for、while语句彼此嵌套时,break语句只应用于最里层的语句,即break语句只能跳出最近的一层循环。 【例3.18】使用break语句终止循环(breakTest.py)。 while True: s = input('请输入字符串(按Q或者q结束):') if s.upper() == 'Q': break print('字符串的长度为:', len(s)) 图316break语句的运行效果 程序运行结果如图316所示。 【例3.19】编程判断所输入的任意一个正整数是否为素数(prime1.py和prime2.py)。 所谓素数(或称质数),是指除了1和该数本身,不能被任何整数整除的正整数。判断一个正整数m是否为素数,只要判断m可否被2~m之中的任何一个整数整除,如果m不能被此范围中任何一个整数整除,m即为素数,否则m为合数。 方法一(利用for循环和break语句): import math #导入模块 m = int(input("请输入一个整数(>1):")) #提示输入整数,并将字符串转换为整数 k = int(math.sqrt(m)) #取整数m的平方根 for i in range(2, k + 2): #判断m可否被2~m平方根之中任何整数整除 if m % i == 0: #整除运算的余数为0,表示可以整除 break #可以整除,肯定不是素数,结束循环 if i == k+1 : #m不能被2~m平方根之中的任何一个整数整除 print(m, "是素数!") #m是素数 else: #m可以被2~m平方根之中的某个整数整除 print(m, "是合数!") #m是合数 方法二(利用while循环和bool变量): import math #导入模块 m = int(input("请输入一个整数(>1):")) #提示输入整数,并将字符串转换为整数 k = int(math.sqrt(m)) #取整数m的平方根 flag = True #先假设所输入的整数为素数 i = 2 #while循环赋初值 while (i <= k and flag == True): #判断循环条件 if (m % i == 0): #整除运算余数为0,表示可以整除 flag = False #可以整除,肯定不是素数,结束循环 else: i += 1 #循环计数值加1,继续循环 if (flag == True): #素数判断标志一直为True print(m, "是素数!") #m是素数 else: #素数判断标志被修改为False print(m, "是合数!") #m是合数 3.4.7continue语句 continue语句类似于break,也必须在for、while循环中使用。但它结束本次循环,即跳过循环体内自continue下面尚未执行的语句,返回到循环的起始处,并根据循环条件判断是否执行下一次循环。 continue语句与break语句的区别在于: continue语句仅结束本次循环,并返回到循环的起始处,循环条件满足的话就开始执行下一次循环; 而break语句则是结束循环,跳转到循环的后继语句执行。 与break语句相类似,当多个for、while语句彼此嵌套时,continue语句只应用于最里层的语句。 【例3.20】使用continue语句跳过循环(continue_score.py)。要求输入若干学生成绩(按Q或q结束),如果成绩<0,则重新输入。统计学生人数和平均成绩。程序运行结果如图317所示。 num = 0; scores = 0; #初始化学生人数和成绩和 while True: #循环条件一直为真 s = input('请输入学生成绩(按Q或q结束):') #提示输入成绩 if s.upper() == 'Q': #输入了Q或q break #跳出循环 if float(s) < 0: #成绩必须>=0 continue #跳转到循环开始,继续判断循环条件 num += 1 #统计学生人数 scores += float(s) #汇总成绩 #输出学生人数和平均成绩 print('学生人数为:{0},平均成绩为:{1}'.format(num,scores / num)) 【例3.21】显示100~200中不能被3整除的数(continue_div3.py)。要求一行显示10个数。程序运行结果如图318所示。 j = 0 #控制一行显示的数值个数 print('100~200不能被3整除的数为:') for i in range(100, 200 + 1): #100~200循环 if (i % 3 == 0): continue #跳过能被3整除的数 print(str.format("{0:<5}",i), end="") #每个数占5个位置,不足后面加空格,并且 #不换行 j += 1 #一行输出的数值个数加1 if (j % 10 == 0): print() #一行显示10个数后换行 图317continue语句的运行效果 图318显示100~200不能被3整除的数 3.4.8死循环(无限循环) 如果while循环结构中循环控制条件一直为真,则循环将无限继续,程序将一直运行下去,从而形成死循环。 程序死循环时,会造成程序没有任何响应; 或者造成不断输出(例如控制台输出,文件写入,打印输出等)。 在程序的循环体中,插入调试输出语句print,可以判断程序是否为死循环。 注意: 有的程序算法十分复杂,可能需要运行很长时间,但并不是死循环。 在大多数计算机系统中,用户可以使用快捷键Ctrl+C中止当前程序的运行。 【例3.22】死循环示例(infinite.py)。 import math #导入模块 while True: #循环条件一直为真 num = float(input("请输入一个正数:")) #提示输入一个正数(浮点数) print(str(num), "的平方根为:", math.sqrt(num)) #输出正数的平方根 print("Good bye!") 本程序因为循环条件为“while True”,所以将一直重复提示用户输入一个正数,计算并输出该数的平方根,从而形成死循环。所以,最后一句“print("Good bye!")”语句将没有机会执行。 3.4.9else子句 for语句和while语句都可以附带一个else子句(可选)。如果for、while语句没有被break语句中止,则会执行else子句,否则不执行。其语法如下。 for 变量 in 可迭代对象集合: 循环体语句(块)1 else: 语句(块)2 或者: while (条件表达式): 循环体语句(块)1 else: 语句(块)2 【例3.23】使用for语句的else子句(for_else.py)。程序运行结果如图319所示。 hobbies = "" #空字符串 for i in range(1, 3 + 1): #循环3次 s = input('请输入爱好之一(最多三个,按Q或q结束):')#提示输入爱好 if s.upper() == 'Q': #输入Q或q,结束循环 break #跳出for循环,执行其后继语句 hobbies += s + ' ' #(爱好)字符串拼接 else: #for语句的else子句 print('您输入了三个爱好。') print('您的爱好为:', hobbies) 图319else子句的运行结果 3.4.10enumerate()函数和循环 Python语言的for循环直接迭代对象集合中的元素,如果需要在循环中使用索引下标访问集合元素,则可以使用内置的enumerate()函数。 enumerate()函数用于将一个可遍历的数据对象(例如列表、元组或字符串)组合为一个索引序列,并返回一个可迭代对象,所以在for循环当中可以直接迭代下标和元素。 【例3.24】enumerate()函数和下标元素循环示例(enumerateTest.py)。程序运行结果如图320所示。 seasons = ['Spring', 'Summer', 'Autumn', 'Winter'] #一年四季的列表清单 for i, s in enumerate(seasons, start=1): #start默认从0开始,现指定从1开始 print("第{0}季节:{1}".format(i, s)) 图320enumerate()函数的 运行结果 3.4.11zip()函数和循环 如果需要并行遍历多个可迭代对象,则可以使用Python内置函数zip()。 zip()函数将多个可迭代的对象中对应的元素打包成一个个元组,然后返回一个可迭代对象。如果元素的个数不一致,则返回可迭代对象的长度与最短的对象相同。 >>> x = [1, 2, 3] >>> y = [4, 5, 6] >>> zip(x, y) #输出:<zip object at 0x000001A68BE80900> >>> list(zip(x, y)) #输出:[(1, 4), (2, 5), (3, 6)] 【例3.25】zip函数和并行循环示例(zipTest.py)。程序运行结果如图321所示。 evens = [0, 2, 4, 6, 8] #偶数 odds = [1, 3, 5, 7, 9] #奇数 for eache,eacho in zip(evens, odds): #将多个可迭代对象中对应的元素打包 print("{0}*{1}={2}".format(eache, eacho, eache*eacho)) 图321zip函数的 运行结果 3.4.12map()函数和循环 如果需要遍历可迭代对象,并使用指定函数处理对应的元素,则可以使用Python内置函数map()。 map(func, seq1[, seq2,…])函数将func作用于seq中的每一个元素,并将所有的调用结果作为可迭代对象返回。如果func为None,作用等同于zip()函数。 例如,如果要返回列表中每个字符串的长度,可以使用内置的map()函数和len()函数。 >>> map(len, ['Spring', 'Summer', 'Fall', 'Winter']) <map object at 0x0000021BA2716CB0> >>> list(map(len, ['Spring', 'Summer', 'Fall', 'Winter'])) #输出:[6, 6, 4, 6] 【例3.26】map函数和循环示例。 >>> list(map(abs, [-1, 0, 7, -8])) #计算绝对值。输出:[1, 0, 7, 8] >>> list(map(pow, range(5), range(5))) #计算乘幂。输出:[1, 1, 4, 27, 256] >>> list(map(ord,'abcdef')) #计算ASCII码。输出:[97, 98, 99, 100, 101, 102] >>> list(map(lambda x,y:x+y,'abc','de')) #字符串拼接。输出:['ad', 'be'] 3.4.13循环语句典型示例代码 使用for语句和while语句都能实现循环功能,具体选择哪种语法构造取决于程序员的偏好。循环语句的典型示例如表32所示。 表32for语句和while语句的典型示例 功 能 示 例实 现 代 码 输出n个数(0~n-1)的2的乘幂的值列表 power = 1 for i in range(n): print(str(i) + " "+ str(power)) power *= 2 输出小于或等于n的最大的2的乘幂的值 power = 1 while 2*power <= n: power *= 2 print(power) 计算并输出1+2+…+n的累加和 total = 0 for i in range(1, n+1): total += i print(total) 计算并输出n的阶乘(n!=1×2×…×n) factorial = 1 for i in range(1, n+1): factorial *= i print(factorial) 输出半径为1~n的圆的周长列表 for r in range(1, n+1): print("r="+str(r), end=" ") print("p="+str(2.0 * math.pi * r)) 3.4.14循环结构综合举例 【例3.27】使用牛顿迭代法求解平方根(sqrtNewton.py)。运行效果如图322所示。 计算一个正实数a的平方根,可以使用牛顿迭代法实现: 首先假设t=a,开始循环,如果t=a/t(或小于容差),则t等于a的平方根,循环结束并返回结果; 否则,将t和a/t的平均值赋值给t,继续循环。 EPSILON = 1e-15 #容差 a = float(input("请输入正实数a:")) #正实数a t = a #假设平方根t=a while abs(t - a/t) > (EPSILON * t): t = (a/t + t) / 2.0 #将t和a/t的平均值赋值给t print(t) #输出a的平方根 【例3.28】显示Fibonacci数列(for_fibonacci.py): 1、1、2、3、5、8、……的前20项。 即F1=1n=1F2=1n=2Fn=Fn-1+Fn-2n>=3 要求每行显示4项。运行效果如图323所示。 图322使用牛顿迭代法求解平方根 图323显示Fibonacci数列 相关语句如下。 f1 = 1; f2 = 1 #赋初值 for i in range(1, 11): #i=1~10循环 print(str.format("{0:6}{1:6}", f1, f2), end=" ")#每次输出两个数,每个数占6位, #空格分隔 if i % 2 == 0: print() #显示4项后换行 f1 += f2; f2 += f1 3.5综合应用: turtle模块的复杂图形绘制 3.5.1绘制正方形(改进版) 【例3.29】修改例2.36的代码,使用循环结构绘制正方形。 import turtle #导入turtle模块 p = turtle.Turtle() #创建海龟对象 p.color("red") #设置绘制时画笔的颜色 p.pensize(3) #定义绘制时画笔的线条宽度 turtle.speed(1) #定义绘图的速度("slowest"或者1) p.goto(0,0) #移动海龟到坐标原点(0,0) for i in range(4): #绘制正方形的四条边 p.forward(100) #向前移动100 p.right(90) #向右旋转90度 3.5.2绘制圆形螺旋 【例3.30】使用海龟绘图分别绘制红、蓝、绿、黄四种颜色的圆形螺旋(spiral.py)。程序运行最终结果如图324所示。 import turtle #导入turtle模块 p = turtle.Turtle() #创建海龟对象 p.speed(0) #定义绘图的速度("fastest"或者0均表示最快) colors = ["red", "blue", "green", "yellow"] #红、蓝、绿、黄四种颜色 for i in range(100): #i=0~99 p.pencolor(colors[i%4]) #设置画笔颜色(红或蓝或绿或黄) p.circle(i) #画圆 p.left(91) #向左旋转91度 图324使用海龟绘图绘制四种颜色的圆形螺旋 3.6习题 扫描下方二维码,下载习题电子版。 扫一扫 习题 3.7上机实践 1. 完成本章中的例3.1~例3.30,熟悉Python语言的三种基本控制结构,即顺序结构、选择结构、循环结构。 2. 编写程序,计算1+2+3+…+100之和。 3. 编写程序,计算10+9+8+…+1之和。 4. 编写程序,计算1+3+5+7+…+99之和。 5. 编写程序,计算2+4+6+8+…+100之和。 6. 编写程序,使用不同的实现方法,输出2000~2500中的所有闰年,程序运行效果如图325所示。 7. 编写程序,计算Sn=1-3+5-7+9-11+…。 提示: 可以使用if i%2==0的语句形式判断i是否为偶数。 8. 编写程序,计算Sn=1+1/2+1/3+…。 9. 编写程序,打印九九乘法表。要求输出九九乘法表的各种显示效果(上三角、下三角、矩形块等方式)。 10. 编写程序,输入三角形三条边,先判断是否可以构成三角形,如果可以,则进一步求三角形的周长和面积,否则报错“无法构成三角形!”。程序运行效果如图326所示(结果均保留一位小数)。 图3252000~2500的所有闰年运行效果 图326三角形周长和面积运行效果 提示: (1) 3个数可以构成三角形必须满足如下条件: 每条边长均大于0,并且任意两边之和大于第三边。 (2) 已知三角形的三条边,则可以利用海伦公式计算三角形的面积=h*(h-a)*(h-b)*(h-c),其中,h为三角形周长的一半。 11. 编写程序,输入x,根据如下公式,计算分段函数y的值。分别利用“单分支语句”、“双分支结构”以及“条件运算语句”等方法实现。程序运行效果如图327所示。 图327分段函数运行效果 y=x2-3xx+1+2π+sinxx≥0ln(-5x)+6x+e4-(x+1)3x<0 12. 编写程序,输入一元二次方程的三个系数a、b和c,求ax2+bx+c=0方程的解。程序运行效果如图328所示。 图328求解一元二次方程 提示: (1) 方程ax2+bx+c=0的解有以下几种情况。 ① a=0andb=0,无解。 ② a=0andb!=0,有一个实根: x=-cb。 ③ b2-4ac=0,有两个相等实根: x1=x2=-b2a。 ④ b2-4ac>0,有两个不等实根: x1=-b2a+b2-4ac2a,x2= -b2a-b2-4ac2a。 ⑤ b2-4ac<0,有两个共轭复根: x1=-b2a+4ac-b22ai,x2=-b2a-4ac-b22ai。 (2) 可以利用“print(str.format("此方程有两个不等虚根: {0}+{1}i和{0}-{1}i", realPart, imagPart))”的语句形式输出方程的两个共轭复根。 13. 编写程序,输入整数n(n>=0),分别利用for循环和while循环求n!。程序运行效果如图329所示。 提示: (1) n!=n×(n-1)×(n-2)×…×2×1。例如,5!=5×4×3×2×1=120。特别地,0!=1。 (2) 一般地,累乘的初值为1,而累加的初值为0。 (3) 如果输入的是负整数,则继续提示输入非负整数,直至n>=0。 14. 编写程序,产生两个0~100(包含0和100)的随机整数a和b,求这两个整数的最大公约数和最小公倍数。程序运行效果如图330所示。 图329阶乘运行效果 图330最大公约数和最小公倍数运行效果 提示: (1) 用户可以利用“random.randint(0,100)”的语句形式生成0~100(包含0和100)的随机整数。 (2) 用户可以直接使用内置函数gcd()求若干整数的最大公约数。 (3) 还可以利用“辗转相除法”求最大公约数,具体算法如下。 ① 对于已知的两个正整数m、n,使得m>n。 ② m除以n得余数r。 ③ 若r≠0,则令m←n,n←r,继续相除得到新的余数r。若仍然r≠0,则重复此过程,直到r=0为止。最后的m就是最大公约数。 (4) 当然,还可以利用“更相减损法”求最大公约数,具体算法如下。 “更相减损法”出自中国古代的《九章算术》,也是一种求最大公约数的算法,具体算法如下: ① 先判断两个正整数的大小,如果两数相等,则这个数本身就是所求的最大公约数。 ② 如果两个正整数不相等,则用大数减去小数,然后用这个较小数与它们相减的结果相比较,如果相等,则这个差就是两个正整数的最大公约数,而如果不相等,则继续执行②操作。 程序实现可参见本章思考题第7题。 (5) 求得了最大公约数后,最小公倍数就是已知的两个正整数之积除以(整除)最大公约数后所得到的商。 15. 假设某理财产品一年期利率为8.75%,编写程序计算多少年后一万元的这款一年期理财产品连本带息才能翻番?程序运行效果如图331所示。 16. 编写程序,输入一个三位自然数,编写程序分别输出其百位数、十位数和个位数上的数字。程序运行效果如图332所示。要求利用“整除运算符//和求余数(求模)运算符%”“divmod()函数”和“map()函数”至少3种不同的编程方法来实现程序的功能。 图331理财产品连本带息翻番的运行效果 图332提取整数各位上数字的运行效果 17. 棋盘放麦粒。在印度有一个古老的传说,舍罕王打算奖赏国际象棋的发明人——宰相西萨·班·达依尔。国王问宰相想要什么,他对国王说: “陛下,请您在这张棋盘的第1个小格里,赏给我1粒麦子,在第2个小格里给2粒,第3小格里给4粒,以后每一小格都比前一小格加一倍的麦子数量。请您把这样摆满棋盘上所有64格的麦粒都赏给您的仆人吧!”国王觉得这要求太容易满足了,就命令给宰相这些麦粒。当人们把一袋一袋的麦子搬来开始计数时,国王才发现,就是把全印度甚至全世界的麦粒全拿来,也满足不了宰相的请求。编写程序显示宰相所要求得到的麦粒总数。程序运行效果如图333所示。 图333棋盘放麦粒的运行效果 18. 自幂数探索。输入一个1~10的整数,编写程序输出相应的自幂数名称、数量和具体的数值,程序运行效果如图334(a)所示。读者可进一步编程,输出1~10所有自幂数的名称、数量和具体的数值清单,程序运行效果如图334(b)所示。 图334自幂数的运行效果 注意: 算法使用逐个数字逐位枚举的方法,因此运行速度会比较慢。如果计算机的性能不够强劲的话,建议不要尝试超过6的自幂数,否则,用户可以使用Ctrl+C组合键中止当前程序的运行。 提示: 如果在一个固定的进制中,一个n位自然数等于自身各个数位上数字的n次幂之和,则称此数为自幂数。 例如,在十进制中,153是一个三位数,各个数位上数字值的3次幂之和为13+53+33=153,所以153是十进制中的自幂数。三位自幂数称为水仙花数。 在n进制中,所有小于n的正整数都为自幂数,比如二进制中1是自幂数,三进制中1和2都是自幂数,四进制中1、2和3都是自幂数,以此类推。 各种自幂数的名称及具体内容如下: 一位自幂数: 独身数。共有9个: 1、2、3、4、5、6、7、8、9。 二位自幂数: 没有自幂数。 三位自幂数: 水仙花数。共有4个: 153、370、371、407。 四位自幂数: 四叶玫瑰数。共有3个: 1634、8208、9474。 五位自幂数: 五角星数。共有3个: 54748、92727、93084。 六位自幂数: 六合数。只有1个: 548834。 七位自幂数: 北斗七星数。共有4个: 1741725、4210818、9800817、9926315。 八位自幂数: 八仙数。共有3个: 24678050、24678051、88593477。 九位自幂数: 九九重阳数。共有4个: 146511208、472335975、534494836、912985153。 十位自幂数: 十全十美数。只有1个: 4679307774。 在十进制中最大的自幂数有39位,共有88个自幂数。 19. 编写程序,利用循环结构,使用turtle库的turtle.fd()函数和turtle.seth()函数绘制一个等边三角形,边长为200像素,程序运行效果如图335所示。 20. 编写程序,利用循环结构,使用turtle库的turtle.right()函数和turtle.fd()函数绘制一个菱形四边形,边长为200像素,程序运行效果如图336所示。 21. 编写程序,利用循环结构,使用turtle库的turtle.fd()函数和turtle.left()函数绘制一个正六边形,边长为100像素,程序运行效果如图337所示。 图335绘制等边三角形 图336绘制菱形四边形 图337绘制正六边形 22. 编写程序,利用循环结构,使用turtle库的turtle.circle()函数、turtle.seth()函数和turtle.left()函数绘制一个四瓣花图形,从左上角花瓣开始,逆时针作画,程序运行效果如图338所示。 23. 编写程序,利用循环结构,使用turtle库的turtle.right()函数和turtle.circle()函数绘制一个四叶草,程序运行效果如图339所示。 24. 编写程序,利用循环结构,使用turtle库的turtle.right()函数和turtle.circle()函数绘制一个星星图形,程序运行效果如图340所示。 图338绘制四瓣花图形 图339绘制四叶草图形 图340绘制星星图形 25. 编写程序,使用海龟绘图绘制一个由蓝色、黑色、红色、黄色和绿色五种颜色组成的奥运五环图形,程序运行效果如图341所示。 图341绘制奥运五环图形 3.8案例研究: 使用嵌套循环实现图像处理算法 在科学计算和各种算法中常常需要使用嵌套循环来处理数据。 例如,图像在计算机中是由像素点组成的二维数组,每个像素点的位置表示为两个整数的元组,像素的值根据图像模式由对应的元组组成(例如RGB模式表示为三个整数值组成的元组,分别表示构成颜色的红、蓝、绿的值,范围为0~255)。 图像处理(例如复制、旋转、裁剪和平滑图像等)的算法根本上就是使用嵌套循环模式对这些像素进行处理。 本章案例研究使用Python第三方图像处理库Pillow中PIL.Image模块的Image类的方法getpixel()和putpixel()来读取和修改特定位置(loc)处的像 素的颜色值(pix),然后使用嵌套循环实现图像处理的基本算法,目的是使学生深入了解Python数据结构和基本算法流程。 本章案例研究的解题思路和源代码等以电子版形式提供,具体请扫描如下二维码。 扫一扫 案例研究