[TOC]
# 第3章 分支结构
# [插画:下述场景]
在前面的学习中,小墨编写的程序都是顺序结构的,今天,他将遇到一种新的程序结构:分支结构。除此之外他还遇到了布尔类型、关系表达式、逻辑运算符等内容,这些内容虽然很有意思,但是也充满了陷阱,他会如何避免呢?最后,墨博士要求他必须做完三道练习才能回家,一起来看看他是怎么解决的吧。
时间:早上9:05
场景:墨馨书屋门外走廊里
小墨匆匆的跑过来...
墨博士:早上好,小墨
小墨:不好意思!今天早上我跟往常一样起床,出门的时候发现下着雨就带了把雨伞,走到半路风太大了,把我伞都吹坏了,我只好去找了家商店重新买了把,结果就迟到了。
墨博士:没关系的,快进来吧。
小墨:博士,今天我们学习什么呢?
墨博士:今天来学习分支结构:前面我们编写的程序都是从上到下顺序执行的,比如有3行代码,程序就会先执行第1行,再执行第2行,然后是第3行,然后程序运行结束。而程序是对现实世界的模拟,现实世界中的事情可不都是“顺序执行”的,比如你早起出门,会根据天气选择带不带雨伞,雨伞坏了会去重新买一把,甚至可以说,你每天都会经历无数的选择。这种选择,在程序中称为**分支结构**。
好了,让我们开始吧。
## 3.1 关系运算符
首先先来做一个练习:
> 编写一个程序,能够输入你的python考试成绩,如果成绩小于60,则输出“成绩不及格”。
小墨:嗯,我想想,先定义一个变量,接收输入的成绩
```
score = input('请输入你的Python成绩:')
```
墨博士:嗯,不错。然后呢?
小墨:然后判断这个输入的成绩和60的关系,小于60的话就使用print语句输出成绩不及格。判断成绩和60的关系在数学上是使用大于、小于,不知道这个在Python中如何表示。
墨博士:思路很清晰。Python中的大于、小于和数学上的一样,如果成绩变量是score,那么表示成绩小于60使用“score < 60”就可以了。
在编程语言中,操作一个以上数据进行运算的符号,称为**运算符**,比如1+2中的+号,是算术运算符。a=2中的=号,是赋值运算符。>和<也是运算符,称为关系运算符。由运算符和操作数组成的式子称为表达式,比如1+2称为算术表达式、a=2称为赋值表达式,score<60称为关系表达式等。
> 墨博士提醒:Python中完整的运算符及使用方法请参考附录B。
python中的关系运算符一共有6个,如表3.1所示。
表3.1 python中的关系运算符
| 关系 | 符号 |
|:-:|:-:|
| 大于 | > |
| 小于 | < |
| 等于 | == |
| 大于等于 | >= |
| 小于等于 | <= |
| 不等于 | != |
小墨:这个等于是使用两个等号吗?
墨博士:还记得我们前面说过的赋值吗,在Python中一个等号“=”表示赋值。因为赋值已经把一个等号“=”给占上了,Python中判断是否等于就用了两个等号“==”。
小墨:哦,原来是这样,我记住了。那这个score > 60如何来用呢?
墨博士:不要着急,我们先来了解一个新的数据类型:布尔类型。
## 3.2 布尔类型
墨博士:以“score<60”为例,这里的score是个变量,表示真正的分数值可以变化,比如score可以是99,也可以是10,可以是任意一个数值,那么“score<60”就只会得到两种结果:
1. 如果score本身大于等于60,那么“score<60”表达式不成立
2. 而如果score本身就是小于60的一个数,那么“score<60”表达式成立
这个成立还是不成立在程序中如何表示呢?看下面这段代码:
```
score = 65
print(score < 60)
score = 55
print(score < 60)
```
运行,结果为:
```
False
True
```
这里的False和True就用来表示score<60是否成立,其中False表示不成立,True表示成立。
从数据类型上来说,这里的True和False,不同于1、2这种整数,也不同于'abc'这种字符串,这是一种新的数据类型,称为布尔(bool、boolean)类型。关系表达式的运算结果就是布尔类型。
小墨:布尔,听起来是个厉害的角色。
墨博士:布尔是19世纪英国的一个伟大数学家的名字,计算机中的逻辑运算称为布尔运算,计算机中的True和False称为布尔值。
![](https://img.kancloud.cn/f9/f9/f9f942f60c92b5135125f9c1b5923bb9_268x326.png)
> 墨博士提醒:如果是写完整的程序,可以通过IDLE启动Python编辑器然后在编辑器中编写代码,如果只是想测试下某个知识点,通过IDLE本身就可以了。这里我们测试下布尔类型,如下:
```
>>> a = True
>>> print(a)
True
>>> b = False
>>> print(b)
False
```
小墨:博士你懂的真多,不过你又啰嗦了,说了这么多,还是没解决最初的练习题呢。
墨博士:下面我们就一起来解决一下吧。
## 3.3 if
墨博士:在python中,条件判断的情况使用if去处理,语法为:
```
if 条件:
语句
```
注意条件后面跟的是英文的冒号,Python中所有的符号都是英文的;第二行语句前是四个空格。Python为了代码的简洁和优美,采用一个Tab或四个空格作为缩进,表示一种所属关系。上面代码就表示如果条件成立,则执行语句,如果不成立,则不执行语句。
小墨:一个Tab键和四个空格是一样的吗?
墨博士:虽然一个Tab键或四个空格都可以用来缩进,但是还是建议你使用四个空格。在IDLE中默认也是四个空格,如图3.1所示。
![图像说明文字](http://p7moyixbi.bkt.clouddn.com/%E5%9B%BE3.1.png)
{-:-}图3.1 IDLE中默认四个空格作为缩进方案
在IDLE中写完if语句后,敲下回车,光标也会自动定位在缩进4个空格处。
接下来我们用if语句把你上述的练习代码补充完整:
```
score = int(input("请输入你的python考试成绩:"))
# 如果成绩小于60分
if score < 60:
print('成绩不及格') # 注意这句前面的4个空格
```
运行,结果为:
```
请输入你的python考试成绩:48
成绩不及格
```
看,我们就把练习的场景模拟出来了。
注意程序中的第1行,用户输入成绩,用score变量接收,由于**input输入默认是字符串类型**,这里要外面包一层int(),将字符串转为int整数类型。第2行以#号开头的,称为程序中的**注释**。所谓注释,就是针对代码的解释说明,它可能是留着自己看,可能是给一块写代码的队友看,总之呢是给人看的(代码是给计算机看的),运行的时候计算机会忽略它。在Python中,单行注释的写法就是#开头,到这一行的末尾结束,一般#号后面会留个空格。单行注释可以加上要注释的内容的上面,也可以加在要注释的内容的后面。
小墨:好的,我已经记住了。
## 3.4 if-else
墨博士:小墨,现在我们来把问题复杂一点:
> 请输入您的python考试成绩,如果成绩小于60,则输出“成绩不及格”,反之则输出“恭喜及格”。
该如何做呢?
小墨:我知道我知道!
```
score = int(input("请输入你的python考试成绩:"))
# 如果成绩小于60分
if score < 60:
print('成绩不及格')
if score >= 60:
print('恭喜及格')
```
墨博士:嗯。这确实是一种办法。除此之外,Python还提供给我们了一种方法,就是if-else结构:
```
if 条件:
执行代码1
else:
执行代码2
```
else表示剩下的、其他的、否则,也即不符合if条件的。它的执行顺序是,如果条件1成立,则执行代码块1,否则,执行代码块2。
小墨:哦,这个好这个好,我写的程序需要判断两次,这个if-else结构只需要进行一次判断就可以了。
墨博士:嗯。上面的问题代码实现如下:
```
# 接收用户输入的成绩
score = int(input("请输入你的python考试成绩:"))
# 如果成绩小于60
if score < 60:
print('不及格') # 打印不及格
# else否则,也即对成绩小于60取反,那就是大于等于60分,就是及格
else:
print('恭喜及格')
```
## 3.5、if-elif-else
墨博士:小墨,现在我们继续升级难度:
> 请输入你的python考试成绩,如果成绩大于80,则输出“优秀”,如果小于等于80但大于等于60,则输出“及格”,如果小于60分则输出“不及格”。
这个如何做呢?
小墨:博士,你先别讲,我猜一下:Python提供了处理两个分支的if-else,那估计也提供的有处理多个分支的情况。
墨博士:是的,如果程序有多个分支,则使用if-elif-else结构,如下:
```
if 条件1:
执行代码块1
elif 条件2:
执行代码块2
else:
执行代码块3
```
elif是else if的缩写,后面也是跟条件,表示在前面if条件不满足的里面挑选符合后跟条件的。
上述if-elif-else结构的执行流程为:如果条件1成立,则执行代码块1,如果条件1不成立,则往下走,**在不符合条件1的成绩里面**判断条件2,如果条件2成立,则执行代码块2,如果条件2也不成立,则执行else下的语句块3。
使用if-elif-else完成上述练习,代码如下:
```
score = int(input("请输入您的python考试成绩:"))
if score > 80:
print('优秀')
# 这里的elif,表示if条件不符合的情况,也即取score小于等于80的,
# 然后在这个小于等于80的成绩中再判断是否大于等于60
elif score >= 60:
print('及格')
else:
print('不及格')
```
小墨:博士,我懂了。并且我已经懂得你的套路了。咳咳,墨博士同学,听好了,下面我们再把问题复杂一下,请输入你的python考试成绩,如果成绩大于等于90,则输出“优秀”,如果小于90但大于等于70,则输出“良好”,如果小于70但大于等于60,输出“一般”,而如果小于60分则输出“不及格”。这个如何做呢?
墨博士:...
小墨:这个依然使用if-elif-else实现,如下:
```
score = int(input("请输入您的python考试成绩:"))
if score >= 90:
print('优秀')
# 这里的elif,是在不符合score >= 90的情况下再判断是否大于等于70
# 也即在score<90的情况下判断是否大于等于70
# 符合题目中如果小于90但大于等于70的要求
elif score >= 70:
print('良好')
# 这个elif,是在不符合条件score>=90,也不符合score>=70的条件下
# 取score>=60,也即取小于70大于等于60的范围
elif score >= 60:
print('一般')
# 这个else是上述三个条件都不成立,也即小于60的情况
else:
print('不及格')
```
墨博士:做的不错!既然你已经掌握了,那我们就来总结一下吧。
上面4个案例就是if语句的全部用法了,总结一下,在if语句中:
* if语句、elif语句后跟条件,当条件满足时执行对应语句块
* 如果所有条件都不满足,则执行else语句块
* if可以单独使用,elif和else不能单独使用
* if出现1次,elif可以出现0-多次,而else可以出现0-1次
## 3.6 if语句使用注意事项
墨博士:小墨,if语句语法简单,也非常常用,后面的几乎每个程序中你都可以看到它的身影。你现在已经掌握了它的基本用法,但还是需要注意下if语句使用时的一些问题。
小墨:嗯,if语句使用时都需要注意哪些问题呢?
### 3.6.1 缩进问题
墨博士:上面我们提到,python中使用缩进表示从属关系,以为了让代码更简洁,比如下面的代码:
```{}
score = 80
if score < 60:
print('不及格')
print('1')
```
输出结果为1
而
```{}
score = 80
if score < 60:
print('不及格')
print('1')
```
则什么都不会输出。原因是第二个程序中的第3和4两行都有缩进,它们组成了一个整体(多行代码一起,称为代码块),都属于if条件的覆盖范围。
而第一个程序中的print('1')这句,由于没有缩进,和前面if就不是从属关系,而是并列关系,是新的一行,所以不受if的影响。
小墨:缩进与否还影响着代码的逻辑,这确实是一个值得注意的问题!
### 3.6.2 变量作用范围问题
墨博士:另一个需要注意的问题跟上面的问题有关,修改上述代码为:
```
score = 50
if score < 60:
s = '不及格'
print(s)
```
运行输出“不及格”,而如果改成这样:
```
score = 50
if score < 60:
s = '不及格'
print(s)
```
运行仍然输出“不及格”。
也就是说:if覆盖范围内定义的变量,在if覆盖范围外仍然可以访问。
小墨:这个问题我也记住了。
墨博士:好,我们继续。小墨同学,既然你已经知道了我的套路,那么现在我就再次将练习难度升级:
现在有python和数学两科成绩,请根据以下评分标准给出成绩的等级。
* 1、python90(含)-100分 并且数学 90(含)-100分 输出:优秀
* 2、python或数学中有一科90分(含)及以上,但是另一科60分以下 输出:偏科
该如何来做呢?
## 3.7 逻辑运算符
小墨:博士,这个还是你来说吧。
墨博士:这个问题我们可以通过if分支结构的嵌套来解决。小墨你听过伪代码吗?
小墨:没有诶。
墨博士:所谓伪代码就是表达思路但是不能运行的代码,来看一个if嵌套的案例的伪代码:
```
if 你英语考了100分:
if 你是中国人:
则说明你外语很好
elif 你是美国人:
则说明你母语很好
```
可以看到英语考了100分是大前提,在此大前提下又区分了你是中国人还是美国人,如果你是中国人,就说明你外语学的不错,如果你是美国人,则说明你母语很好。
小墨:嗯我理解了,if里面还有if,某种情况下又细分了某些情况。
墨博士:是的,这就是if嵌套的思路。理解了这个伪代码之后,我们尝试着解决下上面的练习,代码如下:
```{}
# 定义python的分数
p_score = 58
# 定义数学的分数
m_score = 98
if p_score >= 90: # 如果python分数大于等于90
if m_score >= 90:
print('优秀')
elif m_score < 60:
print('偏科')
elif p_score < 60:
if m_score >= 90:
print('偏科')
```
程序的第5行if p_score >= 90表示在python成绩大于等于90的基础上,执行后面跟的代码块(第6-9行)。
第6-9行又是一个新的if结构,第6行表示如果数学成绩m_score大于等于90,则执行第7行。因为现在的大环境就是python成绩大于等于90,而这里数学成绩如果也满足了大于等于90的条件,当然输出优秀了。
小墨:哦,明白了。同样的第10行表示python成绩小于60的大环境,如果此时数学成绩又大于等于了90分,则说明这位同学偏科。数学好但是python差。
墨博士:是的。除了使用if分支结构的嵌套外,你还可以使用逻辑运算符来解决该问题。
小墨:什么是逻辑运算符呢?
墨博士:逻辑运算符有三种:与、或、非,表示逻辑上的同时成立、一方成立、不成立关系。我们之前曾说过,程序是对现实世界的抽象和模拟,程序中的东西都可以在现实生活中找到原型。下面的句子都是生活中常见的“逻辑”的使用场景:
* 如果你英语考了100分,**并且**你是中国人,则说明你外语很好;
* 明天太热**或**太冷,我都不会出门;
* 如果你成绩**不**好,那你要加油了。
小墨,你能用逻辑词来举个例子吗?
小墨:如果爸爸明天**不**上班,他就会带我去科技馆**或**图书馆。
墨博士:不错的例子。回到Python中,逻辑运算符与、或和非对应的符号为:and(与)、or(或)和not(非)
and的用法如下:
```
a = 2
b = 2
print(a > 0 and b > 0)
print(a < 0 and b < 0)
print(a > 0 and b < 0)
print(a < 0 and b > 0)
```
运行,输出结果为:
```
True
False
False
False
```
小墨,你发现什么规律了吗?
小墨:嗯……,
* 如果and运算的两边都为True,则运算结果为True
* 如果and运算的其中一边为False,则运算结果就为False
是这样的吧博士。
墨博士:是的。再来看or的用法,如下:
```
a = 2
b = 2
print(a > 0 or b > 0)
print(a < 0 or b < 0)
print(a > 0 or b < 0)
print(a < 0 or b > 0)
```
运行输出结果为:
```
True
False
True
True
```
聪明的小墨,这次的规律又是什么呢?
小墨:规律是:
* 如果or的两边有一边为True,其运算结果就为True
* 如果两边都是False,则运算结果是False
没说错吧。
墨博士:没错。最后再来看下not的用法:
```
a = 2
print(not a > 0)
print(not a < 0)
```
运行结果为:
```
False
True
```
小墨,还是你来总结下规律吧。
小墨:not True为False,not False为True。不真就是假,不假就是真。
墨博士:是的,逻辑运算符的计算就这么多了。现在我们用逻辑运算符来解决下前面的练习吧。
代码如下:
```
p_score = 58
m_score = 98
if p_score >= 90 and m_score >= 90:
print('优秀')
elif p_score >= 90 and m_score < 60 or p_score < 60 and m_score >= 90:
print('偏科')
```
小墨:厉害!这种写法比if的嵌套简短了不少,逻辑也清晰了很多。
墨博士:这三种逻辑运算符单独或组合使用,就能解决大部分的逻辑问题,所以有必要好好掌握哦。
## 3.8 逻辑运算符陷阱
墨博士:小墨,逻辑运算符中也有一个陷阱,还是来看一个练习:已知有4个人a,b,c,d,现在4个人进行投票表决一件事情,已知a同意并且b,c,d三人其中一个同意就表示同意这件事,你来补充以下下面的if条件,用于模拟这种同意的条件:
```
a = True
b = True
c = False
d = False
if ____:
print("同意")
```
答案是什么呢?
小墨:这个……,应该填上“a and b or c or d”吧,你看,程序运行确实输出了同意。
墨博士:那如果我们把代码再改以下,你再运行试试:
```
a = False
b = False
c = True
d = False
if a and b or c or d:
print("同意")
```
小墨:咦……怎么回事,运行依然是同意。这个a=False已经说明a不同意了。
墨博士:这显然有逻辑问题了。问题的原因是运算符的优先级问题,就像如下代码:
```
>>> print(1 + 2 * 3)
7
```
运行之所以输出7而不是9的原因就在于1+2*3先计算了2乘以3,结果为6,然后计算了1+6,结果为7。也就是说,乘号*的优先级是比加号+的优先级高的,优先级改变了表达式的运算顺序。
> 墨博士提醒:完整的运算符的优先级介绍请参见附录B
同样,在Python中or运算优先级并不比and高,所以上述a and b or c or d会顺序执行:a先和b用and计算,计算的结果再和c进行or运算,计算的结果再和d进行or运算。针对本题的情况,就出现逻辑问题了。
正确的逻辑应该是a作为一个整体,b or c or d作为一个整体,两者进行and运算。这使用一个小括号提示下优先级就行了,修改后的代码如下:
```
a = True
b = False
c = True
d = False
if a and (b or c or d):
print("同意")
```
小墨:哦,我明白了。
## 3.9 本章小结
墨博士总结:好了,分支结构的内容学到这里就结束了。我来总结一下今天的主要内容:
1、分支结构的完整用法是if-elif-elif-elif-else
2、布尔类型:True和False
3、运算符,尤其是逻辑运算符和关系运算符的使用。关系运算符运算的结果是布尔类型,逻辑运算符运算的结果也是。
4、另外还有一个,昨天我们学习了str()能将整数转为字符串,今天学习了int()能将字符串转为整数。
这些内容都很简单,但是这些是后面的基础,要能够熟练使用。
另外小墨同学,你今天的表现非常棒,值得鼓励。但是你今天要做的还没有结束。编程的核心是编,只有不断的动手去编、大胆的去试,你才能真正的掌握这项技能。下面三个练习,来挑战一下吧。
## 3.10 小墨的练习题
下面是博士给小墨留的三个练习,其中第三题还附带了一个小提示:
练习一:三角形的构成条件是:任意两边之和大于第三边。请编写程序,定义变量a、b和c表示三角形的三条边长,判断这三条边长能够构成三角形。
练习二:录入三个正整数,判断最大的数是多少,并指出第几个数最大。
练习三:输入年份、月份和日期,输出该日期是今年的第几天。需要考虑闰年问题。其中闰年的判断条件是:
1、能整除4且不能整除100
2、能整除400
也就是四年一闰,百年不闰,四百年再闰。
小提示:在Python中如何表示能整除4呢?对4取余,判断结果是否等于0就可以了。代码为:
```
year % 4 == 0
```
其中的%就是Python中的取余运算符(Python中完整的运算符请参考**附录B**)。
# [插画:地球绕太阳转,闰年相关]
> 闰年产生的原因:地球绕太阳运行周期为365天5小时48分46秒(合365.24219天)即一回归年(tropical year)。公历的平年只有365日,比回归年短约0.2422 日,所余下的时间约为每四年累计一天,故第四年于2月末加1天,使当年的历年长度为366日,这一年就为闰年。现行公历中每400年有97个闰年。按照每四年一个闰年计算,平均每年就要多算出0.0078天,这样经过四百年就会多算出大约3天来。因此每四百年中要减少三个闰年。所以公历规定:年份是整百数时,必须是400的倍数才是闰年;不是400的倍数的世纪年,即使是4的倍数也不是闰年。
以下是小墨同学针对三个练习的答案。
练习一小墨的答案:
```
# 定义a、b和c表示三条边
a = 2
b = 5
c = 9
# 三角形的判断条件
if a + b > c and a + c > b and b + c > a:
print('是三角形')
else:
print('不是三角形')
```
练习二小墨的答案:
```
# 定义三个变量接收用户的输入
a = int(input('输入第一个数:'))
b = int(input('输入第一个数:'))
c = int(input('输入第一个数:'))
# 定义变量max_num,表示最大值
max_num = -1
# 定义第几个数是最大值
max_index = 0
# 如果a比最大值max_num大,则将a作为最大值
if max_num < a:
max_num = a
max_index = 1
# 如果b比max_num大,则将b作为最大值,此时max_num是a和b中最大的那个
if max_num < b:
max_num = b
max_index = 2
# 如果c比max_num大,则将c作为最大值,此时max_num是a、b和c中最大的那个
if max_num < c:
max_num = c
max_index = 3
print('第%s个数最大,最大值为:%s' % (max_index, max_num))
```
运行结果为:
```
输入第一个数:5
输入第一个数:4
输入第一个数:8
第3个数最大,最大值为:8
```
练习三小墨的答案:
```
# 定义year、month和day表示输入的年、月和日
year = int(input('请输入年份:'))
month = int(input('请输入月份:'))
day = int(input('请输入日期:'))
# 定义变量february_days,赋值为28
february_days = 28
if year % 4 == 0 and year % 100 != 0 or year % 400 == 0:
# 如果符合闰年的条件,则将february_days更改为29
february_days = 29
# 定义变量days,用来表示这是一年中的第几天
days = 0
# 如果月份是1,则输入的日期就是今年中的第几天,比如1月10日,就是一年中的第10天
if month == 1:
days += day
# 如果月份是2,则输入的日期加上第一个月的31天就是一年中的第几天
elif month == 2:
days = 31 + day
# 如果月份是3,则输入的日期加上第一个月的31天,以及第二个月的天数february_days(可能28,可能29,上面已经计算好)
# 就是一年中的第几天,其他月份依次类推
elif month == 3:
days = 31 + february_days + day
elif month == 4:
days = 31 + february_days + 31 + day
elif month == 5:
days = 31 + february_days + 31 + 30 + day
elif month == 6:
days = 31 + february_days + 31 + 30 + 31 + day
elif month == 7:
days = 31 + february_days + 31 + 30 + 31 + 30 + day
elif month == 8:
days = 31 + february_days + 31 + 30 + 31 + 30 + 31 + day
elif month == 9:
days = 31 + february_days + 31 + 30 + 31 + 30 + 31 + 31 + day
elif month == 10:
days = 31 + february_days + 31 + 30 + 31 + 30 + 31 + 31 + 30 + day
elif month == 11:
days = 31 + february_days + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + day
elif month == 12:
days = 31 + february_days + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + day
# 拼接结果后输出
print('%s年%s月%s日是今年的第%s天' % (year, month, day, days))
```
运行结果为:
```
请输入年份:2018
请输入月份:12
请输入日期:31
2018年12月31日是今年的第365天
```
小读者们,这些练习你有更好的解决办法吗?快来和小墨比比吧。