【Python基础】03 程序的控制流

更新历史:

  • 2021年06月04日完成初稿

  在之前的python程序中,程序是自上而下地逐指令执行,这种方式称为顺序执行,对应的程序结构称为顺序结构。顺序结构在程序中十分常见,但对于一些情况却并不适用,如判断一个三角形的类型、计算1+2+…+100的值等等,这些问题都需要用到下面将要讲述的选择结构和循环结构。另外程序编写时往往会产生意想不到的错误,因此在程序中如何捕捉和处理异常情况也变得尤为重要。

1. 程序的控制结构

  可以证明,在程序设计中,所有程序都可以由以下三种基本结构组成:

  • 顺序结构:自上而下顺序执行代码
  • 选择结构(分支结构):根据条件,有选择地执行不同分支下的代码
  • 循环结构:循环执行循环体内的代码

  选择结构和循环结构会改变程序的执行方向,从而不总是顺序地执行。事实上,函数也会改变程序的执行次序,不过函数是通过调用子函数来实现的,它们会改变程序的控制流,但不是必需的,有关函数的知识在之后的文章里将会详细介绍。

  下面针对选择结构和循环结构进行讲述,通过学习这些控制结构,我们可以编写更有用更复杂的程序。

1.1 选择结构

  选择结构,也被称为分支结构,它为程序设置了不同的分支以根据不同的条件来选择其中的一条分支进行执行。例如,判断一个三角形的类型时,就可以根据三角形三边长度进入不同的分支,从而判断出三角形的类型。

  Python中,选择结构主要有以下几种:

  • 单分支结构:通过if语句实现
  • 双分支结构:通过if-else语句实现
  • 多分支结构:通过if-elif-else语句实现

  选择结构的特点是从不同时执行多条分支,只能选择一条分支执行,哪怕满足多条分支的条件也只选择一条分支。下面就通过「判断三角形的类型」来叙述:

# 选择结构 —— 单分支结构
a, b, c = eval(input("请输入三角形的三边长:"))
if (a + b > c) and (a + c > b) and (b + c > a) :
    print("这是一个三角形!")
请输入三角形的三边长:3, 4, 5
这是一个三角形!

  在这里只有一个if语句,通过判断三角形的三边是否满足「两边之和大于第三边」来确定输入的三边是否能够构成三角形,这里的分支条件是(a + b > c) and (a + c > b) and (b + c > a),其中的and是python中的保留字,表示「与」的意思,与之类似的还有or(或)、not(非)。

扫描二维码关注公众号,回复: 14695868 查看本文章

保留字
  Python中有保留字,也称关键字(keyword),是程序内部定义并保留使用的标识符,因此在程序中不允许定义与这些标识符同名的标识符。Python中有33个保留字:

keyword
False None True and as assert async await
break class continue def del elif else except
finally for from global if import in is
lambda nonlocal not or pass raise return try
while with yield

  在双分支中,除了if语句之外,还有else语句,当不满足if语句时,程序将执行else语句:

# 选择结构 —— 双分支结构(假如三边长能构成三角形)
a, b, c = eval(input("请输入三角形的三边长:"))
if a == b or b == c or c == a :
    print("这是一个等腰三角形!")
else :
    print("这不是一个等腰三角形!")

  不过双分支结构还有一种更为简洁的形式:

    <表达式1> if <条件> else <表达式2>

  如果程序还需要其他分支,则需要使用多分支结构,利用if-elif-else语句实现:

# 选择结构 —— 多分支结构(假如三边长能构成三角形)
a, b, c = eval(input("请输入三角形的三边长:"))
if a == b and b == c and c == a :
    print("这是一个等边三角形!")
elif a == b or b == c or c == a :
    print("这是一个等腰三角形!")
else :
    print("这是一个普通三角形!")

  在上面的多分支结构中,如果a=b=c时,if分支和elif分支都满足,那么程序将顺序执行第一个满足条件的语句,因此if分支和elif分支的顺序是重要的,当不是等边三角形时仍可能是等腰三角形,因此判断等边三角形的语句应该在前。另外elif分支可以是0个或多个,当为0个时即为双分支结构。

  在上面的程序中,设置一个好的条件是非常重要的,在python中也提供了关系运算和逻辑运算,关系运算为值比较提供了方便,而逻辑运算可以对多个条件进行组合:

关系运算符 含义
> 大于
>= 大于等于
< 小于
<= 小于等于
== 等于
!= 不等于

==运算符
  在python中,=是赋值运算符,==是关系运算符,值得注意的是,不要比较两个浮点数相等,因为浮点数在计算机中都是近似存储,比较两个浮点数相等的最好办法是令两个浮点数作差,并小于一个可以接受的精度,例如a - b < 0.000001。

逻辑运算符 含义 真值
and 大于 全真才真
or 大于等于 全假才假
not 小于 真变假,假变真

真值
  在计算机中用非0代表真,用True(值为1的常量)表示,用0代表假,用False(值为0的常量)表示。通过关系运算符和逻辑运算符组成的条件,其真值只有True和False两种,当为True时才执行该条件对应的分支。

  选择结构还有一种常用的用法,那就是嵌套的选择结构,也即选择结构中有选择结构,通过多个if-elif-else语句的嵌套就可以实现复杂的选择分支结构,从而满足不同的需求。

1.2 循环结构

  循环结构是程序中很重要的控制结构,它通过反复执行循环体中的语句来实现相应的功能,根据循环次数是否已知,可以将循环结构分为计数控制的循环和条件控制的循环:

  • 计数控制的循环:用于循环次数已知的情况,用for循环实现
  • 条件控制的循环:用于循环次数未知,通过条件控制循环的结束,用while循环实现

1.2.1 for循环

  for循环通常已知循环的次数,比如计算1+2+…+100的值,就可以利用for循环。for循环的基本用法是:

    for 循环变量 in 可迭代对象 :
      循环体

  例如下面的程序:

result = 0
for i in range(1, 101, 1) :
    result += i
print("1+2+...+100 = {}".format(result))
1+2+...+100 = 5050

  对于for循环而言,最大的疑问就是「什么是可迭代对象?」,可迭代对象是指可以按次序迭代(循环)的对象,包括序列、字符串、字典、文件等等,上面的range()就可以产生一个可迭代的对象,它在for循环中十分常用。

range()
  range()的原型是range(start, end, step),表示从start(默认为0)开始,到end(不包含end)结束,步长为step(默认为1),可以产生一个(start,start+step,…)的对象,而for i in range(1, 101, 1)就是让i从1开始,每次递增1,直至大于等于101才结束。

1.2.2 while循环

  while循环则是条件控制的循环,循环的结束与否是通过判断是否满足循环条件,而往往不知道循环的次数,其主要用法为:

    while 循环条件 :
      循环体

  仍然以计算1+2+…+100的值为例,用while循环改写上面的程序:

result = 0
i = 1
while i <= 100 :
    result += i
    i += 1
print("1+2+...+100 = {}".format(result))
1+2+...+100 = 5050

  在这里,while循环的循环条件是i <= 100,当满足条件时则执行循环体中的内容,否则跳出循环。在while循环中,循环变量i是一定要变化的,否则容易造成无限循环,此时终止条件永远不满足,比如上面程序中若缺少i += 1,那么程序将无限地执行下去,这种情况称为「死循环」,无论对于初学者还是资深的程序员而言,总是会犯这样的错误,因此需要多加小心。

死循环
  当处于死循环中,可以通过键盘输入Ctrl+C结束当前进程。

1.2.3 循环的扩展模式

  在python中,还提供了break和continue保留字以用来辅助循环的执行:

  • break:退出当前层次的循环
  • continue:终止此次循环,开始下一次循环

  下面就介绍一下break语句和continue语句的一些作用:

result = 0
for i in range(10) :
    if i % 2 == 0 :
        continue
    result += i
print(result)
25

  在上面程序中,当i是偶数(if i % 2 == 0 )时则执行continue语句,此时会跳出本次循环,而不执行之后的result += i语句,而是直接执行下一次循环(相当于跳过循环体内continue之后的所有语句),因此程序实际上计算的是1+3+5+7+9。

  对于break而言,则有所不同,其会直接终止本层循环,相当于不执行本层循环了,具体如下所示:

result = 0
for i in range(10) :
    if i > 5 :
        break
    result += i
print(result)
15

  当i大于5时,执行break语句,此时结束for循环,因此上面实际计算的是1+2+3+4+5。不过当有多个循环时,break只能结束离其最近的一个循环,而不是所有循环。总而言之,break语句和continue语句提供了结束循环的两种方式,但想结束某个循环或者跳过某层循环时,break语句和continue语句是一个很好的选择。

  针对两种循环,python还提供了循环的扩展模式,即在一般的循环结构上还增加了else子句,其语法形式为:

    for 循环变量 in 可迭代对象 :
      循环体
    else :
      else子句

    while 循环条件 :
      循环体
    else :
      else子句

  当循环正常退出时(包括执行continue语句),则执行else子句,当循环遇到break、return和异常情况而退出时则不执行else子句。

2. 异常处理

  异常是程序执行过程中发生,会影响程序正常执行的事件,一般情况下,在python无法正常处理程序时就会发生一个异常。异常表示一个错误产生,当Python脚本发生异常时我们需要捕获处理它,否则程序会终止执行。

  尽管异常和错误是不同的概念,但在python中,异常大多数与程序设计错误有关,程序设计错误主要有以下几种:

  • 语法错误:违反python语法规则而导致的错误,如漏写了冒号等
  • 运行时错误:运行程序时发生的错误,如除0错误等
  • 逻辑错误:程序逻辑上的错误,如本来应该是“ > ”,而误写成了“ < ”,这类错误不能被编译器和解释器直接发现,

异常和错误
  从能否捕获的角度,语法错误和运行时错误能被捕获,因此可以认为是异常,而逻辑错误很难被捕获,因此被认为是错误。

  Python中用异常对象(exception object)来表示异常情况,遇到错误时如果异常对象没有被捕捉和处理时,程序就会用回溯(Traceback)错误信息来给出提示并终止程序的执行。

2.1 捕捉和处理异常

  Python中主要有以下几种异常的情况,每种异常都有一个异常类名,在程序中便可通过异常类名来捕捉常见的异常情况,主要的异常类名有:

异常类名 含义
Exception 常规异常
AttributeError 对象不存在此属性
IndexError 序列中无此索引
IOError 输入/输出操作失败
NameError 找不到名字(变量)
SyntaxError python语法错误
TypeError 对类型无效的操作
ValueError 传入无效的参数
ZeroDivisionError 除(模)运算的第二个参数为0

异常类名
  上面的异常类名是python中内建的异常类,可以直接书写并使用,同时其也是大小写敏感的,应正确地书写。

  接下来则叙述如何捕捉异常,python中提供了try-except语句去捕捉异常情况,使用try-except语句时,即使出现错误,程序也将继续进行,并显示友好的错误信息,而不是令用户迷惑的Traceback。try-except语句的基本用法是:

    try :
      被检测语句块
    except 异常类名 :
      异常处理程序

  被检测的语句块放在try语句块中,异常处理程序放在except块中,若被检测的语句块中没有异常,则忽略except后的异常处理,否则执行异常处理,如下面的程序所示:

try :
    a, b = eval(input("Please enter two numbers: "))
    print(a // b)
except ZeroDivisionError :
    print("Division by zero!")
Please enter two numbers: 1, 0
Division by zero!

另外,在程序中except语句块可以有多个,通过设置多个except子句来捕捉不同的异常,当然也可以在except后列出异常类名,例如 except (ValueError, ZeroDivisionError, …) : 语句。当except后面为空时,则捕捉全部异常,不过更多时候还是希望能够发现并识别异常的类型,那么就可以使用下面的程序:

try :
    ......
except 异常类名 as 错误原因名 :
    ......
    print(错误原因名)

  上面的程序相当于将异常类名起一个别名,然后将该异常情况(具体的错误原因)打印出来,一般情况下,更适合用Exception捕捉常规异常,然后打印出具体的错误原因,如下所示:

# 推荐使用的异常处理程序
try :
    a, b = eval(input("Please enter two numbers: "))
    print(a // b)
except Exception as err :
    print(err)
Please enter two numbers: !, 0
invalid syntax (<string>, line 1)
>>> 
Please enter two numbers: 2, 0
integer division or modulo by zero
>>> 

2.2 异常处理子句

  在python中,try-except语句块中还可以两种子句:

  • else子句:当try语句块中没有异常引发,则执行else子句,否则不执行
  • finally子句:无论是否发生异常,都会执行finally子句

  在程序中,这两种子句可以同时使用,如下所示:

try :
    a, b = eval(input("Please enter two numbers: "))
    c = a // b
except Exception as err :
    print(err)
else :
    print("{} // {} = {}".format(a, b ,c))
finally :
    print("Program end!")
    # exit(0)、return、break...
Please enter two numbers: 1, 0
integer division or modulo by zero
Program end!
>>>
Please enter two numbers: 1, 2
1 // 2 = 0
Program end!
>>> 

3. 程序设计风格

  在前文的Python实例中,程序实例开始变得复杂,因此有必要在这里总结一些关于python程序设计的知识。

  程序由一条一条的「语句」构成,语句是完整执行一个任务的一行逻辑代码,例如 print(1 + 2) 是一条打印语句,在python中写在一行的可执行指令即为一条语句,语句之间的层次结构由「缩进」来表示,例如:

if a > b :
	return a

  在这里,if a > b 和 return a 都是语句,它们有一定的执行层次,当 a > b 时才执行 return a ,因此在 if a > b 后面有一个冒号(半角符号),在 return a 前面有一定的空格(通常为4个英文空格,即一个Tab键)。在python中," : "标记一个新的逻辑层, 增加缩进表示进入下一个逻辑层,减少缩进则为返回上一个逻辑层,而相同逻辑层保持相同的缩进。

逻辑层
  逻辑层可以理解为占一行的语句,python中可以用" ; "来声明语句的结束,以至于一行上有多条语句也是允许的,但在这里,其仍然被视为”占有多行“的语句,例如a = 1; b = 2; 等价于分别占有一行的a = 1和b = 2。

  对于一条语句而言,其由数据和运算符组成:数据一般有常量(1、1/3等)、变量(a、b等),常量和变量都由值和类型构成,数据类型决定了数据的存储方式,主要由整型、浮点型、字符型等。运算符则包括赋值运算符(=)、算术运算符(+、-)等等。

  除此之外,在程序中往往会对关键代码作一些注释,即程序编写人员对于程序的解释,通常会在程序关键的位置进行说明,主要有以下两种形式:

  • 单行注释:# 这是单行注释
  • 多行注释:用'''表示注释的开始结束

  通过这些基本的构件,便可以组成复杂的程序,但对程序本身而言,一股脑地写出所有执行的指令是不实际的,往往会对程序有所封装,这就需要下文所介绍的函数了。

猜你喜欢

转载自blog.csdn.net/Stu_YangPeng/article/details/117635687