上一节,我们剧透了如何用分支条件结构,判断用户可能的输入错误,给出不同输出结果。
x = input('请输入一个整数:')
if x.isdecimal():
x = int(x)
print(type(x)) # 结果int
else:
print('输入错误,请重新执行程序。')
这是Python经典的if-else分支结构语句:为了让程序能在各种可能情况下良好执行,就需要在代码中预先判断。
分支条件结构是预判的一种方式。还有一种叫异常,我们在后面章节会提到。
除了if-else之外,Python主要的语句结构有5种:
- 顺序结构:最简单形式,一个接一个执行;
- if判断结构:“有条件就上,没条件就过”;
- if-else判断结构:“有条件就上,没条件想办法也要上”;
- for-in循环:“挨个点名,不遗漏”;
- while循环:“有条件就别停,一遍接一遍地做”。
所有的程序,都可以用这5种结构组合而成。这就是结构化编程。
本节内容:
- 分支条件结构:单分支、多分支、嵌套分支
- 两种循环结构:for-in循环、while循环
- 应用案例:设计实现一个简易计算器
1、分支条件结构
1.1 单分支
如果是单独分支,可以用if语句判断后执行。
x = 2
if x > 0:
print('x大于0') # 会执行分支
注意其中的写法:
- 语句后面带冒号(英文的)
- 判断后的执行语句,需要缩进,一般是4个空格
- x、0和大于号之间不一定需要空格,但添加后更美观
1.2 双分支
如果有两个分支,可以用if-else语句。
x = 2
if x <= 0:
print('x不大于0')
else:
print('x大于0') # 执行这个分支
注意其中else后面也有英文冒号。
1.3 多分支
如果有更多分支,可以用if-elif-else语句。
x = 2
if x < 0:
print('x小于0')
elif x == 0:
print('x等于0')
elif x == 1:
print('x等于1')
elif x == 2:
print('x等于2') # 执行这条分支
else:
print('x大于2')
同样的elif语句末尾也有英文冒号,有多少额外情况,就添加多少elif分支语句。
在写条件判断语句时,要注意程序是从上往下一个个判断的,当条件一旦成立,它就执行对应分支,然后就忽略其他分支,继续往下执行。所以在写的时候一定要确保自己的逻辑正确。
举个错误例子:
# 错误例子
x = 3
if x < 0:
print('x小于0')
elif x > 0:
print('x大于0')
elif x > 2:
print('x大于2')
这段程序中,缺了else分支是可以的。但有两个错误:
- 最后一个分支永远不会被执行,因为第二个分支会100%把它“截胡”了:大于2的数一定也大于0。
- 没有判断等于0的情况,当x是0时,不会输出任何东西,很多情况下,并不是设计者故意为之,而是遗漏。
所以,在写分支判断结构时,要记住2个原则:
- 条件覆盖所有情况,哪怕某些情况下不用做什么,也建议至少思考一遍,比如用pass来代表不作为
- 去掉那些已经被其他分支覆盖的,没意义的分支,让自己看得更清
- 区分条件时,按序列出组合情况,比如数字从小到大,字母从a到z等
比如,上面的错误案例,可以改成如下:
# 错误修正例子
x = 3
if x < 0:
print('x小于0')
elif x == 0:
pass # 用pass代表不做什么
else:
print('x大于0')
1.4 嵌套分支
当分支的执行语句也需要条件判断时,可以嵌套来使用:
x, y = 3, 4
if x < 0:
print('{}<0'.format(x))
if y >= 0:
print('{}<0<={}'.format(x,y))
elif x < y:
print('{}<{}<0'.format(x,y))
else:
print('{}<={}<0'.format(y,x))
else:
print('{}>=0'.format(x))
if y < 0:
print('{}<0<={}'.format(y,x))
elif x > y:
print('{}>{}>=0'.format(x,y))
else:
print('{}>={}>=0'.format(y,x)) # 这个分支执行
1.5 三元表达式
最后,关于条件判断,Python有一个三元表达式,上一节中也剧透过
x = input('请输入一个整数:')
print(type(int(x))) if x.isdecimal() else print('输入错误,请重新执行程序。')
它的结构是:a if 条件判断 else b,再看几个例子
x = 1 if 2 > 1 else 0 # 1
print('hello' if len('hello') > 5 else 'python') # python
它等价于下面的分支写法:
if 2 > 1:
x = 1
else:
x = 0
if len('hello') > 5:
print('hello')
else:
print('python')
其中,len()是Python的一个内置函数,用来获取集合类数据长度,这里就是获得字符串长度。
三元表达式的意义,是为了让一些很浅显的判断不用写的太复杂,不用占用太多行。
当然,一开始你不习惯,可以放一边,以后遇到分支条件的时候看看,能否写成三元表达式,慢慢训练和寻找感觉。
2、两种循环结构
Python的循环其实不止两种,但这里我们重点介绍99%情况下会用到的for-in和while。
想想看:怎么把一个字符串反过来输出?
2.1 切片器
上一节中,你是否留意字符串处理中的一个技巧?
s = 'hello python'
print(s[::-1])
这是字符串切片器的作用。
切片操作有三个参数 [start: stop: step],表示从start位置到stop位置,输出每个元素,每次前进step步。
当然,每个参数都是省去而使用默认,比如:
s = 'hello python'
s[1:] # 表示从第2个元素(下标为1)开始取所有,'ello python'
s[:2] # 表示从第1个元素(下标为0)开始取到第3个元素(不含),'he'
s[2::2] # 表示从第3个元素(下标为2)开始取所有,每次步进2,'lopto'
s[-1:-5:-1] # 负数代表从尾巴开始数,从最后一个元素,取到倒数第5个(不含),每次倒退1,'noht'
有点迷糊?没关系,它是python实现的一种“工具”,方便我们迭代访问元素。
2.2 for-in迭代
回到问题:字符串反转,我们看看Python的for-in循环怎么写。
s1 = 'hello python'
s2 = '' # 定义一个空字符串
for c in s:
s2 = c + s2
print(s2)
for-in语句注意事项:
- 注意在语句后添加英文冒号
- 循环内语句需要缩进
这个循环,每次从s1里取一个字符,然后把它添加到s2的前面,最后实现了反转,示意图如下。
我们再来看另一种写法。
写之前,介绍2个内置函数enumerate()和range()。
enumerate(x)可以为一个“可迭代对象”生成对应的序号。
s1 = 'hello python'
s2 = ''
for i, c in enumerate(s):
s2 = c + s2
print('经过第{:2d}次迭代: s2 = {}'.format(i+1,s2))
注意:生成的序列和下标一致,也是从0作为第1个元素开始的。
再看另一种写法,我们先用range()函数生成一个整数序列(准确叫法叫迭代器),然后用下标来获取元素。
s1 = 'hello python'
s2 = ''
for i in range(len(s1)):
s2 = s1[i] + s2
print('经过第{:2d}次迭代: s2 = {}'.format(i+1,s2))
我们让range()函数生成了从0到s1长度(即12,但不包含)的整数序列,再通过下标获得每个字符。
输出内容和上面写法完全一样,但其实range()写法更灵活。
比如,我们想把从第2个元素开始,所有偶数位的字符取出来。
s = 'hello python'
for i in range(1, len(s1), 2):
print('第{:2d}位字符: {}'.format(i+1,s[i]))
range()函数的完整参数包括3个:
range(start, stop[, step])
看,是不是和切片器参数很像?
好了,for-in循环先掌握这些,足够你应付一大波情况。
2.3 while循环
再看Python的另一个常用循环:while。
我们也设置一个问题:计算从1到100(包含)的数字之和。
可能你很快就心算出来:(1+100)*100/2,不过计算机可没那么聪明,除非你告诉它这个“算法”。
如果不知道这个算法,也可以用循环解决。
n = 1
sum = 0
while n <= 100:
sum += n
print(sum)
当然了,在这么简单的问题下,你也可以用for-in解决。
sum = 0
for i in range(101):
sum += i
print(sum)
如果稍微把问题改一下:把从1开始递增的连续整数累加起来,使其不超过1000,问和是多少?
1,2,3,4……N,把这些连续整数加起来,但你无法提前知道N是多少,这时候while会比for-in更适合。
n, sum = 1, 0
while sum + n <= 1000:
sum += n
n += 1
print(sum, n) # 990,45
于是,你知道从1累加到44,不超过1000。
如果非要用for-in也可以,因为你可以猜大概的数字。
sum = 0
for i in range(1, 40):
if sum + i > 1000: # 判断还能不能往上加
break # 表示提前退出循环
sum += i
else: # 表示完整迭代所有数字没有break情况
for i in range(40, 80):
if sum + i > 1000:
break
sum += i
else:
print('还需要更多数字')
print(sum)
我们第一次估计N=40,但是发现全加完后可能还不够,于是再往后猜40个数字……直到满足靠近但不超过1000。
这里,我们用到了for-in-else循环的完整结构,当循环中没有提前break,循环完毕后就会执行else分支。
到这里你应该可以感受到:for-in更适合已知长度的迭代,while更适合未知长度循环。
3、应用案例:计算器
最后,我们来设计一个自己的计算器,实现以下功能:
- 加减乘除,以及取余数和乘方
- 输出一张九九乘法表
3.1 分析问题
先来分析下问题,这也是编程第一件要做的事情。
- 怎样设计输入?
- 怎样计算?
- 怎样输出?
我们知道输入的都是字符串,但怎样判断“加减乘除”等运算符呢?
你也许会想到,用字符串的find查找功能,但你如何解决运算优先级呢,比如有括号?
最爽的用户体验,就是输入任何一个表达式,就可以计算出结果。
但目前你可能束手无策,于是,你就只能牺牲用户的交互体验了。
事实上,任何一个好算法,都是一点点从最差的迭代而来。
这是一种通用解决问题的思路:渐进迭代。
3.2 解题思路
所以,我们先按我们能力范围,设计解题思路:把每一种操作定义为一种选择,根据用户的选择来确定采取的计算方式,再让用户输入两个数字,由此计算输出答案。
建议根据今天的内容自己实现一遍。
3.3 参考代码
print('请输入一个选项(整数序号):')
print('1、加法')
print('2、减法')
print('3、乘法')
print('4、除法,如不能整除,输出商和余数')
print('5、乘方')
print('6、打印九九乘法表')
choice = ''
while not choice.isdecimal() \
or int(choice) > 6 or int(choice) < 1:
# 太长的语句,我们用空格加反斜杠来换行
choice = input('请重新输入序号:')
choice = int(choice)
# 除了第6个选择外,其他都需要输入两个数字,我们可以先判断特例
if choice == 6:
for i in range(1,10):
for j in range(1,i+1):
# print函数的end参数控制输出尾巴,'\t'如同你按Tab,默认是换行
print("{} * {} = {}".format(j, i, i*j), end='\t')
print('')
else:
x, y, s = '', '', ''
while not x.isdecimal or not y.isdecimal():
s = input('请输入两个整数,以空格分离:')
if s.count(' ') != 1: # 字符串的count函数统计某个字符出现几次
# 有且只有一个空格,否则不继续本次执行,重新进入下轮循环
continue
x, y = s.split(' ')
x, y = int(x), int(y)
if choice == 1:
print('{} + {} = {}'.format(x, y , x + y))
elif choice == 2:
print('{} - {} = {}'.format(x, y , x - y))
elif choice == 3:
print('{} * {} = {}'.format(x, y , x * y))
elif choice == 4:
if y == 0: # 除数不能是0
print('除数为0') # 这里也可以重新让用户输入
elif x % y == 0: # 能整除
print('{} / {} = {}'.format(x, y , x + y))
else: # 不能整除,输出表达式
print('{} = {} * {} + {}'.format(x, y, x//y, x % y))
elif choice == 5:
print('{} ^ {} = {}'.format(x, y , x ** y))
else: # 这里不会出现,谨慎起见可以输出一行提示
print('Error condition')
注意点:
- 循环和条件判断可以互相嵌套
- 如果发现很多分支内存在同样代码,说明可以合并,见输入两个整数部分
总结
本节我们重点学习了Python的分支条件结构和循环结构。
顺序执行、分支条件结构、循环结构,构成了程序的执行流程,所有程序流程都可以由这三类组合而成。
作者:程一初
更新时间:2020年8月