CHAPTER 第3章 判断与决策———选择程序设 计 学习目标: .理解算法的概念和算法的描述方法。 .理解关系运算、逻辑运算和条件运算。 .掌握几种形式的逻辑判断条件。 .能用选择结构解决逻辑判断与决策问题。 .理解复合语句(语句块)的概念。 .熟悉现有各种运算的优先级。 通过第2章的入门学习,大家已经能够用计算机解决一些比较简单的问题了。请大家 先回顾一下第2章所解决的问题的特点:先给定一些数据(键盘输入或程序中用赋值语 句), 然后按照某个公式计算出一些结果,最后把结果输出到屏幕上,告知用户。这个过程可 以说是直线型的,很固定,它们的先后顺序是固定不变的、依次进行的,在这个过程中不需要 做任何判断,没有任何智能在里面,对应的程序结构是顺序结构。实际上计算机不仅能计 算,按照公式计算,而且还能够有选择地、有判断地采纳不同的计算方案,也就是计算机具有 判断决策能力,能像人一样思考。当然,这种能力是人通过程序赋予计算机的。本章要展现 的就是在Python中是如何表示判断条件的,是如何做判断决策的。 本章要解决的问题是: .让成绩合格的学生通过; .按成绩把学生分成两组; .按成绩把学生分成多组(百分制和五分制); .判断某年是否为闰年; .判断点的位置。 1 让成绩合格的学生通过 3. 问题描述: 假设有一个计算机打字训练教室,大一刚入学的同学都要到这个训练教室练习打字。 计算机自动考核,成绩60 分以上视为合格。训练教室的门口有一个计算机控制的栏杆,它 是一个“智能栏杆”,知道每一个参加训练的同学的当前训练成绩,因此当有人走进它时,它 第3章 判断与决策———选择程序设计 63 会获取学号,并要求输入成绩,然后计算机会检查输入的成绩是否属实,如果属实并大于或 等于60分,栏杆将自动打开,允许通过。可想而知,如果成绩小于60分智能栏杆会是什么 样子。注意,键盘输入成绩的时候必须要诚实,别忘了它是智能栏杆,不然就会有不良记录。 写一个程序模拟这个“智能栏杆”。 输入样例1: What is your grade? 88 输入样例2: What is your grade? 50 输出样例1: Good! You passed! … 输出样例2: 无 问题分析: 这个问题看起来似乎很复杂,怎么搞一个“智能栏杆”呢? 首先,“智能栏杆”知道前来测 试的同学的成绩,可以用input函数来模拟,即等待同学输入他/她的成绩;控制栏杆的起落 可以用一个简单的判断来模拟,即“成绩是大于或等于60吗”,如果是,它就升起,允许同学 通过,待同学通过之后又落下,等待其他同学的到来。它始终处于监控状态。这里“允许通 过”可以用输出一条信息“Good! Youpassed!”来仿真。因此,整个过程就可以先用input 获取成绩,获得成绩后,进行一个判断,如果成绩大于或等于60,打印通过信息;不然什么都 不做,继续等待同学输入,这个过程是无限的。图3.1是解决这个问题的流程图,下面的算 法是解决这个问题的简单版本,没有考虑任何可能的输入错误,而且程序不能停止。 图3.1 让成绩合格的学生通过的流程图 算法设计: ① 计算机等待输入成绩; ② 如果成绩大于或等于60,输出“Good! Youpassed!”,回到①; ③ 否则,回到①。 程序清单3.1 1 #@File: passedsimple.py 2 #@Software: PyCharm 3 64 问题求解与Python程序设计 4 while True: 5 grade = int(input("What is your grade? ")) 6 if grade >=60: 7 print("Good! You passed!") 修改一下上面的算法,让它具有基本的容错能力,当输入非数值数据时提示输入错误重 新输入,当输入Ctrl-Z或Ctrl-D(Linux系统)时程序停止运行,自然流程图也要修改一下, 读者可以尝试一下。修改后的实现代码如下: 程序清单3.1-1 1 #@Fle: passedgood.py 2 #@Software: PyCharm 3 4 while True: 5 try: 6 grade = int(input("What is your grade? ")) 7 if grade >=60: 8 print("Good! You passed!") 9 except EOFError: #处理输入Ctrl-Z/Ctrl-D 时产生的EOFError 异常 10 break 11 except ValueError: #处理输入非数值数据时产生的ValueError 异常 12 print("Your input is not right!") 13 continue 14 15 print("\nBye!") 3.1.1 关系运算与逻辑判断 程序清单3.1中第6行有一个式子grade>=60,它是关系表达式,其中出现的运算符 >=称为关系运算符。这个表达式是把grade变量引用的对象与60进行大于或等于比较, 比较的结果有两种可能,或者为真,或者为假,当大于或等于成立时为真,否则为假。由此可 见,关系表达式的真假是表示逻辑判断的条件,使用关系表达式就可以让计算机具有一定的 “智能”。 Python支持6种关系运算,其中大于>,小于<,大于或等于>=,小于或等于<=,它 们与数学上两个数的比较运算>、<、≥、≤相对应,但要注意写法上有所不同。此外,还有 等于==,不等于!=两种运算,这与数学上的写法大不相同。这里有两个容易犯的错误,一 是把由两个符号构成的关系运算如<=分开书写成< =,中间多了一个空格;二是非常容 易把判断相等的关系运算==写成一个=号,这两个都是语法错误。 关系运算像算术四则运算一样,都是双目运算,即它们都有两个操作数。由关系运算符 连接起来的式子称为关系表达式,如grade>=60就是一个关系表达式。 到此为止,已经有三类主要的运算了,它们是算术运算、赋值运算、关系运算。在一个表 达式中既可以出现算术运算,也可以出现关系运算,甚至它们的混合运算,因此不同类型的 运算符之间必须规定严格的优先级。即使是同一类运算,不同的运算之间也要有严格的结 第3章 判断与决策———选择程序设计 65 合性。这三类运算的优先级是: 关系运算的优先级低于算术运算,但高于赋值运算,而关系运算中比较大小的四个运算 >,<,>=,<=的优先级又高于判断相等的两个运算==,!=。 关系运算是左结合的,但一般很少连用,真正使用的时候多加一层括号更清楚。表3.1 扩展了表2.2,给出了当前各种运算符的优先级和结合性。 表3-1 运算的优先级和结合性(优先级从高到低) 运 算 符含 义结 合 性 () 括号最近的括号配对 +,- 单目运算,取正、负从右向左 *,/,% 双目运算,乘、除、求余从左向右 +,- 双目运算,加、减从左向右 >,<,>=,<= 双目运算,比较大小从左向右 ==,!= 双目运算,判断是否相等从左向右 = 双目运算,赋值从右向左 【例3.1】 设有“a=1,b=2,c=3”,分析下面两个语句中各种运算的顺序: ① print("%d\n"%(a+b>c)); //算术运算与关系运算混合 ② status = a >b; //赋值运算与关系运算混合 分析如下:因为算术运算优先于关系运算,所以①的运算顺序为先a+b,其结果再与c 比较;因为关系运算优先于赋值运算,所以②的运算顺序为先a>b,结果再赋值给status。 【例3.2】 设有“a=30,b=20,c=2”,下面语句正确吗? status = a >b >c; 如果正确,status的值会是多少? Python中连续比较运算相当于两个比较之间省略了一个并且运算,即 a >b >c 等价于a >b 并且b >c 其中30>20为真,20>2为真,因此status结果为真。 3.1.2 逻辑常量与逻辑变量 任何表达式都是有值的,算术表达式的值是算术运算的结果,赋值表达式的值是赋值的 结果。Python中关系表达式的值应该是比较的结果。关系表达式比较的结果只有两种可 能,不是真就是假。在计算机内部用1表示逻辑真,用0表示逻辑假。在Python中分别对 应逻辑类型bool的逻辑常量True和False,这是两个符号常量。例如: >>>a=10 >>>b=20 >>>a>b False >>>a<=b True >>>a==b False 66 问题求解与Python程序设计 Python语言提供逻辑数据类型bool,它实际上是整数类型int的子类,例如 >>>status = a >=b >>>atatus False >>>type(status) >>><class 'bool'> >>>print("%d"%status) 0> >>status = a <b >>>print("%d"%status) 1 或者使用int转换器直接输出逻辑常量True和False的值。 >>>print(int(True)) 1 >>>print(int(False) 0 3.1.3 单分支选择结构 关系表达式的真或假构成了逻辑判断的条件。Python的选择结构就是通过判断条件 的真和假有选择地执行某些语句(分支)。按照分支的多少分为三种选择结构:单分支、双 分支和多分支。本节介绍单分支的选择结构。 单分支选择结构用if语句表达,具体格式如下: if 判断条件表达式: 条件为真时执行的语句或语句块 其他语句 其中判断条件表达式可以是3.1.2节介绍的关系表达式,也可以是其他形式(详见3.1.4 图3.2 单分支选择结构流程图 节);条件为真时执行的语句可以是任何可执行语句,甚 至可以是多个语句构成的语句块(也称为复合语句)。 单分支选择结构的流程图如图3.2所示。 注意if语句的结构和写法。从结构上来看,可以认 为if语句有两部分组成,一部分是if判断行,if之后跟 一个判断表达式,该表达式不用外加圆括号,另一部分 是条件为真时要执行的语句,这一部分可以是另一个单 句,也可以是一组语句。但是要注意两点,一是if判断 行必须以冒号“:”结尾,二是第二部分的语句块必须按 缩进格式书写。因为Python用缩进格式对语句进行分 块,所以缩进的距离要相同。程序清单3.1的第6行和7 行合起来才是一个单分支选择结构,或者叫if语句。注 意它的写法: >>>if grade>=60: >>> print("Good! You passed!\n") 程序结构通常用流程图表示,可以直观地认识它的执行过程,这个if语句对应的流程 图如图3.1或3.2所示。在流程图中,一般把判断条件表达式置于菱形框之内,用菱形框表 示判断条件,所以通常称菱形框为判断框。判断框有且只有一个入口,有且只有一个可以选 择的出口,“真”或者“假”。框图中的两个小圆圈表示在其之前或之后是程序的其他部分,它 第3章 判断与决策———选择程序设计 67 是连接其他部分的关节,称为连接框。从框图可以看出if语句的执行过程是:条件为真时 执行某个可执行语句或复合语句;条件为假时跳过那个可执行语句或复合语句去执行if结 构下面的其他语句。 【例3.3】 对键盘输入的两个整数a和b,输出它们所具有的大小关系。 先简单分析一下。两个数的大小关系可能有多种,大于、小于、大于或等于、小于或等 于、不等于、等于,不能只给出其中的一种判断。如果大于关系成立,大于或等于也成立,不 等于也成立。输入的数据不同,大小关系也不同。因此需要列出所有可能的大小关系。具 体实现见程序清单3.2。 程序清单3.2 1 #@File: compare2numbers.py 2 #@Software: PyCharm 3 ''' 4 Enter 2 integer numbers, output the comparing results 5 ''' 6 a, b = eval(input("Enter two numbers e.g.,2,3")) 7 if a >b: 8 print("%d >%d"%(a,b)) 9 if a >=b : 10 print("%d >=%d"%(a,b)) 11 if a == b: 12 print("%d == %d"%(a,b)) 13 if a <b: 14 print("%d <%d"%(a,b)) 15 if a <=b: 16 print("%d <=%d"%(a,b)) 17 if a !=b: 18 print("%d !=%d"%(a,b)) 运行结果: 输入用例1: 2 3 输入用例2: 3 2 输入用例3: 3 3 输出用例1: 2<3 2<=3 2!=3 输出用例2: 3>2 3>=2 3!=2 输出用例3: 3>=3 3<=3 3==3 68 问题求解与Python程序设计 复合语句是多个语句的复合体,也可称为语句块,在Python中通过缩进格式表示复合 语句。复合语句本身自成一体,它与程序的其他部分既相互独立又有一定的联系。一个单 分支的选择结构,常常包含一个复合语句在内,下面的例子中9行到11行构成一个复合语 句,是条件grade<60为真时要执行的语句。还有其他的语句块,你能识别出来吗? 【例3.4】 写一个程序统计不及格的学生数。 程序清单3.3 1 #@File: nopassed.py 2 #@Software: PyCharm 34 nopassed = 0 5 while True: 6 try: 7 grade = eval(input("What is your grade? ")) 8 if grade <60 : 9 print("Sorry! You are not passed!") 10 print("Hope you make great efforts!") 11 nopassed = nopassed +1 12 except EOFError: #输入Ctrl-Z 或Ctrl-D 时,退出while 13 break 14 except NameError: #如果输入了非数值的字符串,产生这个异常 15 print("Your input is not right!") 16 continue 17 18 print("\nNopassed total:", nopassed) 19 print("Bye!") 注意:Nopassed变量是统计不及格人数的累加变量,它必须初始化为0。 3.1.4 特殊形式的判断条件 除了利用像grade>=60这样的关系表达式的值作为逻辑判断条件之外,还有一些特 殊形式,它们不是关系表达式,但当它们用作判断条件时,系统就会把它们转换为逻辑值。 如算术表达式的值、一个整型变量或常量的值、一个字符常量值,甚至是浮点型变量或常量。 Python规定一个表达式的值或某个变量的值或常量,当它们出现在if语句或其他含有判断 条件的语句中时,只要它们的值非零,就转换为逻辑真,只有它们的值为零时才为逻辑假。 简单来说,非0即为逻辑真,0为逻辑假。下面分别举例说明。 【例3.5】 判断一个整数不是偶数(或者是奇数)。 一个数x如果它能被2整除它就是偶数,也就是说如果x%2等于0,那么x就是偶数, 那么一个数不是偶数的判定条件为真怎么写呢? 答案是x%2!= 0,即x除以2其余数不 为零,不为零的数当然不等于0,因此结果为真。下面的if语句 >>>if x%2 !=0: >>> print("%d 不是偶数"%x) 按照非零即为逻辑真的原则,可以把它简写为 第3章 判断与决策———选择程序设计 69 >>>if x%2: >>> print("%d 不是偶数"%x) 这里用算术表达式x%2作为逻辑判断条件,当它的值非零时就转化为逻辑真。 【例3.6】 判断一个整数不是零。 很容易写出“一个整数x不是零”为真的条件x!=0,因此有下面的if语句: >>>if x !=0: >>> print("%d 不是零"%x) 与例3.4类似,同样有下面的简写形式 >>>if x: >>> print("%d 不是零"%x) 这里直接使用了变量的值作为判断条件,当x不是零时它的值就转换为逻辑真。 现在回头看看while1,这个1就是一个常量作为逻辑判断条件的例子,1不为零,所以当 然就是逻辑真了。其实,如果把1换成任何一个其他不为零的整数,负数也可以,其效果都是 一样的,如while2,while-100。而且这个常数作为逻辑条件是不变的,所以它永远为真。 还有更多形式的判断条件后面章节会陆续介绍。 3.1.5 比较两个实数的大小 在数学上,比较两个实数与比较两个整数没什么区别,但在计算机中,比较两个实数要特 别小心了。由于实数在计算机中存储是有精度的,Python中的float类型的数据只有16位或 17位有效数字,而且可靠的有效位数只有15位,所以超出部分的不同就不起作用了。例如: >>>a=3.12345678901234569 >>>b=3.12345678901234561 >>>a==b True 不难看出,虽然浮点型a和b它们的第18个数字不同,在数学上这是两个不相等的实 数,但是在Python中它们确相等。再如: >>>a=0.123456789012345675 >>>b=0.123456789012345672 >>>a==b True 同样,a和b小数点后第18个数字不同,但它们的前17位相同,所以Python认为它们相 等。因此两个浮点型数是否相等是由它们的精度决定的。浮点数float的默认精度是16位到 17位,在实际问题中未必需要这么高的精度。精度常常用一个小数表示,如0.01就是精确到2 位小数,0.0001就是精确到4位小数,当小数位数更多的时候可以用指数形式表示,如.1e-7就 是精确到第8位小数,这种精度数据常常用变量epsilon(可简写为eps)表示,即 epsilon = .1e-7; 70 问题求解与Python程序设计 Pythonsys模块中给出一个float型数据的epsilon,即 >>>sys.float_info.epsilon 2.220446049250313e-16 这个数对应的普通小数就是 0.0000000000000002220446049250313 在程序设计的时候一般不是直接比较两个实数是否相等,而是通过它们之间的误差的 精度来判断,误差如果在允许范围之内就认为是相等的了。标准数学模块math中提供了 一个求float数据的绝对值的函数fabs(doublex),在程序中用来求两个数的误差(就是相 减)的绝对值,如果这个绝对值不超过给定的精度epsilon,就可以认为这两个数是相等的。 我们可以检验一下上面相等的a和b是否满足这个条件: >>>math.fabs(a-b)<sys.float_info.epsilon True 把a和b的值修改成 >>>a=0.123456789012345685 >>>b=0.123456789012345672 或者 >>>a=0.123456789012345785 >>>b=0.123456789012345572 可以运行>>>math.fabs(a-b)<sys.float_info.epsilon检验一下。 看一个完整的例子,程序清单3.4中给定了一个精度eps=0.001和单精度的pi= 3.1415926,用户任意输入一个pi值存入yourPi中,程序通过把yourPi与程序中的pi值比 较,如果它们的误差的绝对值不超过给定的精度eps,这时就认为yourPi符合精度,也可以 认为在给定的精度下yourPi与程序中的pi相同。如果它们的误差的绝对值大于给定的精 度,这时认为yourPi不符合精度要求。 程序清单3.4 1 #@File: realNumCompare.py 2 #@Software: PyCharm 3 4 import math 5 myEpsilon = 0.001 6 myPi = 3.1415926 7 yourPi = eval(input("I have a epsilon, Enter your Pi:")) 8 err = math.fabs(myPi -yourPi) 9 print("the err is:", err) 10 if err <=myEpsilon: 11 print("Your Pi is ok! Because of the err %7.5f <=myEpsilon %7.5f" \ %(err, myEpsilon)) 第3章 判断与决策———选择程序设计 71 12 if err >myEpsilon: 13 print("Your Pi is not ok! Because of the err %7.5f >myEpsilon %7.5f"\ %(err, myEpsilon) ) 运行结果1: I have a epsilon, Enter your Pi:3.1415 the err is: 9.259999999988722e-05 Your Pi is ok! Because of the err 0.00009 <=myEpsilon 0.00100 运行结果2: I have a epsilon, Enter your Pi:3.14 the err is: 0.001592599999999944 Your Pi is not ok! Because of the err 0.00159 >myEpsilon 0.00100 3.2 按成绩把学生分成两组 问题描述: 教师要把参加某次测验的学生按成绩及格与否分成两组,并统计出各组的人数。 输入样例: 88 99 77 66 55 44 -1 //-1 表示输入结束 输出样例: you belong in group A you belong in group A you belong in group A you belong in group A you belong in group B you belong in group B aNum = 4 bNum = 2 问题分析: 3.1节的问题只考虑成绩合格者如何处理,成绩不合格者置之不理,即当判断条件为真 时去处理事情,而当判断条件为假时就跳过了,没有做任何事情。很多场合我们不仅要描述 判断条件为真时做什么,还要对判断条件为假时的情况做出处理。本节的问题仍然是一个 判断决策问题。按学生成绩把学生进行分组,就是成绩大于或等于60的学生去A 组,成绩 小于60的学生去B组,并统计出每组的学生数。用简单的单分支选择结构可以解决这个 问题吗? 回答是可以的。分析下面的程序是否可行,看看存在什么不足。 程序清单3.5 1 #@File: if2parallel.py 2 #@Software: PyCharm 3 aNum = 0 72 问题求解与Python程序设计 4 bNum = 0 5 while True: 6 try: 7 grade = eval(input("Enter grades:")) 8 if grade >=60: 9 print("You belong in Group A") 10 aNum = aNum +1 11 if grade <60: 12 print("You belong in Group B") 13 bNum = bNum +1 14 except EOFError: 15 break 16 except NameError: 17 print("Your input is not right!") 18 print() 19 print("aNum = ", aNum) 20 print("bNum = ", bNum) 这个实现的正确性是没有问题的,但仔细看看会发现,不管成绩是大于或等于60,还是小 于60,第8行和第11行的两个判断都要进行。例如,现在一个学生的成绩是90,首先经历第8 行的判断,grade>=60为真,执行第9行和第10行。紧接着就要执行第11行的判断,grade <60为假,因此不执行第12行和第13行。同样如果一个成绩是50,也要经历同样的两次判 断。每个成绩都要判断两次,显然是一种浪费。实际上,成绩大于或等于60和成绩小于60这 两个判断条件之间是紧密相连的,是恰好相反的。如果第一个条件为假,自然就有另一个条件 为真,没有必要再去重复判断。对于具有这样性质的判断问题,Python提供了一种双分支选 择结构if-else。下面用双分支的选择结构解决这个问题,其流程图如图3.3所示。 图3.3 按成绩把学生分成两组的流程图 第3章 判断与决策———选择程序设计 73 算法设计: ① 把统计求和变量aNum,bNum 初始化为0。 ② 输入学生成绩,如果输入了Ctrl-Z或Ctrl-D,执行⑤,否则③。 ③ 如果成绩大于或等于60,输出分到A 组信息,aNum 加1,返回②。 ④ 否则,输出分到B组信息,bNum 加1,返回②。 ⑤ 输出最终统计结果,程序结束。 程序清单3.6 1 #@File: ifelse.py 2 #@Software: PyCharm 3 aNum = 0 4 bNum = 0 5 while True: 6 try: 7 grade = eval(input("Enter grades:")) 8 if grade >=60: 9 print("You belong in Group A") 10 aNum = aNum +1 11 else: 12 print("You belong in Group B") 13 bNum = bNum +1 14 except EOFError: #输入了Ctrl-Z 或Ctrl-D 15 break 16 except NameError: 17 print("Your input is not right!") 18 print() 19 print("aNum = ", aNum) 20 print("bNum = ", bNum) 图3.4 双分支选择结构流程图 3.2.1 双分支选择结构 程序清单3.6的第8行到第13行是一个双分 支的选择结构,条件grade>=60为真时执行一个 分支,否则执行另一个分支。双分支选择结构用ifelse 语句表示,其一般形式为: if 判断条件表达式: 条件为真时要执行的语句 else: 条件为假时要执行的语句 其他语句 其中表达式和语句的含义同单分支选择结构一样。 它的执行过程如图3.4所示,当判断条件为真时执 行if和else之间的语句,否则(隐含着判断条件为 74 问题求解与Python程序设计 假),执行else后面的语句。在这个结构中存在两个分支,对于给定数据,只能有一个分支 符合判断条件。不管是哪个分支,执行完毕之后都应该执行if-else结构下面的“其他语句”。 这种双分支选择结构是对称的。 从程序清单3.5和程序清单3.6的运行结果可以看到,两个平行的单分支选择结构和一 个双分支选择结构都能实现本节的问题,其结果完全一致,但是它们的运行过程有很大的不 同。两个单分支要判断两次,而一个双分支只判断一次。下面再看几个小例子。 【例3.7】 判断一个数是奇数还是偶数。 程序实现的片段如下: num = eval(input()) if num %2 : print("num is odd") else: print("num is even") 当用户输入一个整数之后,如果输入的是奇数,则num%2不为0,判断条件为真,输出 信息numisodd,如果输入的是偶数,num%2为0,判断条件为假,输出numiseven。输出 哪条信息(进入哪个分支)由判断条件num%2的真假决定。 【例3.8】 判断一个数是大于或等于零还是小于零。 程序实现的片段如下: num = eval(input()) if num >=0: print("num is equal to 0 or positive number ") else: print("num is a negative number") 【例3.9】 判断一个人的体重是否过大,判断标准是身体指数t是否大于25,其中t= w/h2(w 为体重,h为身高)。 程序实现的片段如下: w,h=eval(input()) t = w/(h*h) if t >25 : print("your weight is higher!"); else: print("your weight is not higher,\ but I could not know if your weight is lower!") 3.2.2 条件表达式 由于双分支选择结构应用比较频繁,Python提供了一种特别的运算,称为条件表达式, 用来表达对称的双分支选择结构,具体格式如下: 表达式1 if 逻辑表达式2 else 表达式3 第3章 判断与决策———选择程序设计 75 这里有三个表达式,两个关键字,可以把它理解成一个三目运算。这个表达式的执行顺 序是先执行if运算,判断逻辑表达式2的真假,如果为真则执行表达式1,否则执行表达式 3。注意表达式2是判断条件,表达式1和表达式3是对称的两个选择结果。条件表达式的 使用非常灵活简洁,通常可以用于有选择的赋值给某个变量。下面看几个例子。 【例3.10】 求两个数的最大值,对于给定的a和b, max =a if a >b else b max的值是条件表达式aifa> belseb的结果,而条件表达式由逻辑表达a>b的真假决 定,当a>b时条件表达的值为a,否则为b。 【例3.11】 用条件表达式判断print函数的输出内容是奇数还是偶数。 print("num is odd" if num%2 else "num is even") 条件表达式的值是print函数的输出值,它由逻辑表达式num%2的真假来决定是表达式1 的字符串numisodd还是表达式2的字符串numiseven。类似的还有下面的例子。 【例3.12】 用条件表达式判断print函数输出的一个数是正还是负的信息。 print("zero or positive" if num >=0 else "negative") 【例3.13】 打印两个数中的较大者,对于给定的i和j。 print(i if i >j else j) 根据i>j的真假,输出i或j,当i>j时输出i,否则输出j。 【例3.14】 返回两个数中的最大者,对于给定的i和j。 return( i if i >j else j) 根据i>j的真假,返回i或j,当i>j时返回i,否则返回j。 注意:条件表达式一般多用于三个表达式比较简单的情形,并且可以嵌套。 【例3.15】 条件运算是右结合的,对于给定的a、b、c、d条件表达式。 a if a >b else c if c>d else d 按照右结合的原则,c> d先作为第二个条件表达式的判断条件,如果它为真取c,否则取 d,然后再看a>b是否为真,如果为真,则取a,否则取c>d时条件运算的值。它相当于 a if a >b else ( c if c>d else d ) 3.3 按成绩把学生分成多组(百分制) 问题描述: 写一个程序帮助教师把学生按分数段(90分以上,80~89分,70~79分,60~69分,小 于60分)分成多组,并统计各组的人数。 输入样例: please input grades: 76 问题求解与Python程序设计 44 55 77 88 99 98 78 67 Ctrl-D 输出样例: Failed! group F Failed! group F Middle! group C Better! group B Good! group A Good! group A Middle! group C Pass! group D aNum = 2 bNum = 1 cNum = 2 dNum = 1 FNum = 2 问题分析: 3.2节已经使用双分支选择结构解决了把学生按成绩分为两组的问题。本节的问题要 求进行更细致的划分,即根据成绩的分数段(90分以上,80~89分,70~79分,60~69分,小 于60分)把学生划分为多个组,不妨设为A、B、C、D、F组。并分别统计各组的人数。问题 中有多个判断条件,而且不是简单的关系表达式所能表达的。90分以上和60分以下比较 容易表达,其他几种情况都是一种复合条件,即两个条件要同时满足,如成绩介于80分到 89分之间的复合条件是“成绩大于或等于80分”并且“成绩小于90”,在Python中允许 写成 if 80 <=grade <90: 这种判断方法中每个分数段用一个if,采用顺序并列的方式,实现所有分数段的划分。或者 把它拆成下面3.3.1节将讨论的嵌套的if结构来表达。读者可以尝试一下这种判断方法, 并分析它有什么不足。 本问题的不同分数段的判断条件实际不是独立的,如90分以上的分数段与不是90分 以上的彼此具有else的关系,因此可以用双分支的选择结构,即ifgrade>=90…else…。 而else里面还可以有进一步的判断,这是双分支的嵌套。因此按照相邻分数段之间彼此存 在的这种联系即可实现,另外本节成绩分数段的判断,不一定必须先判断哪个分数段,后判 断哪个,但判断顺序的不同,将导致不同的嵌套顺序。算法设计1是按照成绩从大到小的顺 序进行判断的,有比较整齐的嵌套描述。即先判断成绩是否大于或等于90分,如果不是,再 看是否大于或等于80分,以此类推。而算法设计2是按成绩的客观分布规律考虑判断的顺 序,即可能性比较大的先判断。首先判断成绩是否介于70分和80分之间,如果不是,则有 两种可能,一是大于或等于80分,二是小于70分。如果是大于或等于80分,进一步看是否 小于90分,又分两种情况,小于90分和大于或等于90分;如果是小于70分,进一步看是否 大于或等于60分,这时又有两种情况,小于和大于或等于60分。 算法设计1:流程图如图3.5所示。 第3章 判断与决策———选择程序设计 77 图3.5 按成绩把学生分成多组算法1的流程图 ① 把统计求和变量aNum,bNum,cNum,dNum,fNum 初始化为0; ② 输入学生成绩; ③ 如果输入没有结束则执行④否则执行⑨; ④ 如果成绩大于或等于90,输出分到A 组信息,aNum 加1,返回到②; ⑤ 否则如果成绩还大于或等于80,输出分到B组信息,bNum 加1,返回到②; ⑥ 否则如果成绩还大于或等于70,输出分到C组信息,cNum 加1,返回到②; ⑦ 否则如果成绩还大于或等于60,输出分到D组信息,dNum 加1,返回到②; ⑧ 否则输出分到F组信息,fNum 加1,返回到②; ⑨ 输出统计结果 程序清单3.7 1 #@File: ifelseif.py 2 #@Software: PyCharm 34 aNum = bNum = cNum = dNum = fNum = 0 56 while True: 7 try: 8 grade = eval(input("Please enter your grades:")) 9 if grade >=90: 10 print("Good! Group A") 11 aNum = aNum +1 78 问题求解与Python程序设计 12 elif grade >=80: 13 print("Better! Group B") 14 bNum = bNum +1 15 elif grade >=70: 16 print("Middle! Group C") 17 cNum = cNum +1 18 elif grade >=60: 19 print("Pass! Group D") 20 dNum = dNum +1 21 else: 22 print("Failed! Group F") 23 except NameError: 24 print("Your input not right!") 25 except EOFError: 26 break 27 print() 28 print("aNum = ", aNum) 29 print("bNum = ", bNum) 30 print("cNum = ", cNum) 31 print("dNum = ", dNum) 32 print("fNum = ", fNum) 算法设计2:流程图如图3.6所示。 图3.6 从最可能的成绩开始判断 ① 把统计求和变量aNum,bNum,cNum,dNum,fNum 初始化为0; ② 输入学生成绩; ③ 如果输入没有结束则执行④,否则执行⑨ ④ 如果成绩小于80且大于或等于70,输出分到C组信息,cNum 加1,返回到②; ⑤ 否则如果成绩小于90且大于或等于80,输出分到B组信息,bNum 加1,返回到②; ⑥ 否则(成绩大于或等于90)输出或分到A 组信息,aNum 加1,返回到②; 第3章 判断与决策———选择程序设计 79 ⑦ 否则如果成绩小于70且大于或等于60,输出分到D组信息,dNum 加1,返回到②; ⑧ 否则(成绩小于60),输出分到F组信息,fNum 加1,返回到②; ⑨ 输出统计结果。 程序清单3.8 1 #@File: ifelseifbetter.py 2 #@Software: PyCharm 34 aNum = bNum = cNum = dNum = fNum = 0 56 while True: 7 try: 8 grade = eval(input("Please enter your grades:")) 9 if grade <80: 10 if grade >=70: 11 print("Middle! Group C") 12 cNum = cNum +1 13 elif grade >=60: 14 print("Pass! Group D") 15 dNum = dNum +1 16 else: 17 print("Failed! Group F") 18 fNum = fNum +1 19 elif grade <90: 20 print("Better! Group B") 21 bNum = bNum +1 22 else: 23 print("Good! Group A") 24 aNum = aNum +1 25 except NameError: 26 print("Your input is not right!") 27 except EOFError: 28 break 29 print() 30 print("aNum = ", aNum) 31 print("bNum = ", bNum) 32 print("cNum = ", cNum) 33 print("dNum = ", dNum) 34 print("fNum = ", fNum) 3.3.1 嵌套的if结构 什么是嵌套呢? 简单来说,就是两个东西彼此套在一起,是一种包含关系。一个单分支 的if能和另一个if套在一起吗? 让我们仔细分析一下单分支的if结构。if条件为真时要 执行的某些语句并没有规定必须是什么语句,当然也可以是另一个if结构,即 80 问题求解与Python程序设计 if 表达式1: if 表达式2: 表达式2 为真时执行的语句 这样两个if结构彼此就嵌在一起了,外层if语句的表达式1为真时要执行的语句是内层的 另一个if语句。 这种嵌套结构表达了一种复合条件,即如果表达式1为真,并且表达式2也为真,则执 行“表达式2为真时要执行的语句”。 【例3.16】 下面的程序片段表达了什么判断? if grade >=60: if grade <70 : print("Passed!"); 其含义是,如果成绩大于或等于60分,并且又小于70分,计算机回答“Passed!”。这样 就实现了判断成绩是否介于60和70之间。请注意这种嵌套的书写格式,它是逐层缩进的 格式,如 果把本节问题中的各个分数段分别进行判断,那么介于80和90之间的判断就可以 用一个独立的if嵌套实现,同样介于70和80之间都可以用一个独立的if嵌套实现等,问题 便可以得到解决,这种方法的完整实现大家作为一个练习尝试一下。 3.3.2 嵌套的if-else结构 与单分支的if结构嵌套类似,if-else结构同样可以嵌套,而且更加灵活方便,其结构如 图3.7所示。当if判断条件为真时要运行的语句或者else部分要运行的语句,都可以是另 一个if或if-else结构。双分支的if-else是对称结构,if部分和else部分都可以再嵌套其他 的选择结构。if-else结构的if部分嵌套另一个if-else的基本框架如下: if 表达式1: if 表达式2: 表达式2 为真时执行的语句 else: 表达式2 为假时执行的语句 else: 表达式1 为假时执行的语句 if-else结构的else部分嵌套另一个if-else的基本框架如下: if 表达式1: 表达式1 为真时执行的语句 else: if 表达式2: 表达式2 为真时执行的语句 else: 表达式2 为假时执行的语句 可以想象,if部分或else部分的if-else还可以进一步嵌套其他的选择结构,这样的嵌套 第3章 判断与决策———选择程序设计 81 可以包含多层,如图3.7所示。不管内部嵌套了多少层,从最外层来看就是一个if-else结 构。对于本节的分组问题,用if-else嵌套实现如下: if grade >=90: print("Good! Group A") aNum = aNum +1 else: if grade >=80: print("Better! Group B") bNum = bNum +1 else: if grade >=70: print("Middle! Group C") cNum = cNum +1 else: if grade >=60: print("Pass! Group D") dNum = dNum +1 else: print("Failed! Group F") 图3.7 嵌套的if-else结构 这个嵌套结构在grade>=90不为真时内嵌了一个另外的if-else结构,内嵌的if-else 进一步判断grade>=80是否为真,当grade>=80为假时又内嵌了一个if-else结构,判 断grade>=70是否为真,当grade>=70为假时可以继续嵌套另一个if-else结构,判断 grade>=60是否为真,当grade>=60为假时都归结为F组,嵌套结束。这样的嵌套可能 很多层,你认为这种形式的嵌套选择结构有什么不足吗? 还有一点非常值得注意,嵌套的if-else结构,虽然层次可以很多,但是Python语言的