控制结构是编程中的基础概念,它允许开发人员控制程序执行的流程。控制结构使程序能够根据条件做出决策,重复执行操作,并根据运行时遇到的条件执行不同的操作。控制结构具有重要意义,它们为创建能够动态响应变化条件的复杂程序提供了基础。没有控制结构,程序将仅限于按固定顺序执行指令,这将使创建能够解决现实世界问题的复杂应用变得困难。
在Python编程中,有三种基本类型的控制结构:顺序结构、分支结构和选择结构。
程序流程图是一种用图形、流程线和文字说明描述程序基本操作和控制流程的方法,它是程序分析和过程描述的最基本方式。程序流程图又称程序框图,是用统一规定的标准符号描述程序运行具体步骤的图形表示。程序框图的设计是在处理流程图的基础上,通过对输入输出数据和处理过程的详细分析,将计算机的主要运行步骤和内容标识出来。
流程图的基本元素包括7种,如图3.1所示。其中,起止框表示一个程序的开始和结束;判断框判断一个条件是否成立,并根据判断结果选择不同的执行路径;处理框表示一组处理过程;输入/输出框表示数据输入或结果输出;注释框增加程序的解释;流向线以带箭头直线或曲线形式指示程序的执行路径;连接点将多个流程图连接起来,常用于将一个较大的流程图分隔为若干部分。
图3.1 流程图的基本元素
绘制流程图时,为了提高流程图的逻辑性,应遵循从左到右、从上到下的顺序排列。程序流程图示例如图3.2所示:找出A、B、C三个数中最大数的处理过程。流程图在分析、设计、记录及操控许多领域的流程或程序都有广泛应用。它能让思路更清晰、逻辑更清楚,有助于程序的逻辑实现,有效解决实际问题。
图3.2 流程图示例
程序控制结构有顺序结构、分支结构和循环结构三种。
1)顺序结构是最基本的控制结构,计算机程序一条一条顺序执行代码,如图3.3所示。这种结构通常用于简单的程序,不需要做出决策或重复执行操作。单一的顺序结构解决不了所有问题,需要引入分支结构和循环结构来更改程序的执行顺序,以满足多样的功能要求。
2)分支结构允许程序根据条件做出决策,选择下一步要执行的指令,如图3.4所示。这种结构通常用于需要根据不同情况执行不同操作的程序。
图3.3 顺序结构
图3.4 分支结构
3)循环结构使程序能够多次重复执行一组指令,如图3.5所示。这种结构通常用于需要重复执行相同或类似操作的程序。这就像我们每天都在进行工作和学习的循环,但每天看似重复的工作产生的价值和对自我的提升是不一样的。
图3.5 循环结构
在Python中,单分支语句指的是if语句。if语句用于根据指定的条件来决定是否执行一组语句。如果条件为真,则执行if语句块中的语句;否则,跳过if语句块。if语句的语法格式如下:
语句块是if条件满足之后执行的一个或多个语句序列,语句块中的语句通过与if所在行形成缩进表达包含关系。if语句中语句块执行与否依赖于条件判断。但无论是什么情况,控制都会转到if语句后与if语句同级别的下一条语句。
if语句中的条件可以使用任何能够产生True或False的语句或函数。形成判断条件最常见的方式是采用关系操作符。Python语言中共有6个关系操作符,见表3.1。需要注意的是,Python中“=”是赋值语句,“==”表示等于。
表3.1 if条件中常用操作符
实例3.1展示了单分支结构的应用,演示了如何使用单分支结构来判断智能车电池电压是否满足运行要求。智能车比赛中使用的镍氢电池电压范围为8~16V,而智能车微处理单元的工作电压为10~13V。为了保证智能车稳定运行,在开机之前需要检查电池电压,实际使用中电池电压由电压读取电路获得。
实例设计来源:在2023年智能车大赛前一晚,某学生给电池充了一整晚的电,但第二天参加比赛的时候,小车跑得出乎意料的慢。学生分析可能是电压过高引起的,当然也可能是其他原因造成的,但是赛前检查电池电压是非常重要的。
Python中if-else语句用来形成二分支结构,该语句用于根据指定的条件来决定执行哪一组语句。如果条件为真,则执行if语句块中的语句;否则,执行else语句块中的语句。语法格式如下:
实例3.2使用二分支结构实现了实例3.1相同的功能,即检查电池电压是否满足智能车运行要求。
二分支结构还有一种更简洁的方式,适合通过判断返回特定值,语法格式如下:
实例3.3通过这种简洁方式实现了与实例3.1相同的功能。
多分支结构允许程序根据多个条件来选择不同的执行路径。在Python中,可以使用if-elif-else来构造多分支结构。基本语法格式如下所示:
多分支结构是二分支结构的扩展,这种形式通常用于设置同一个判断条件的多条执行路径。Python依次评估寻找第一个结果为True的条件,执行该条件下的语句块,结束后跳过整个if-elif-else结构,执行后面的语句。如果没有任何条件成立,那么else下面的语句块将被执行,else语句可选,即else语句根据编程实际情况可有可无。
实例3.4使用多分支结构实现智能车电池电压检查。
Python 3.10增加了match...case的条件判断,这里不做详细介绍,有兴趣的同学可以自学。
综合二分支结构和多分支结构,实例3.5实现了一个识别用户输入来执行加、减、乘、除四则运算的简单计算器。
循环结构是一种常用的程序控制结构,它允许程序重复执行一段代码。循环结构在程序设计中具有重要意义,它为程序员提供了一种灵活、高效、易于理解和维护的方式来处理复杂的逻辑判断和数据。它在各种应用领域都有广泛应用,包括游戏开发、数据分析、人工智能、机器学习等。
根据循环执行次数的确定性,循环可以分为确定次数循环和非确定次数循环。
1)确定次数循环指循环体对循环次数有明确的定义,这类循环在Python中称为遍历循环,采用for语句实现,循环次数采用遍历结构中的元素个数来体现。
2)非确定次数循环指程序不确定循环体可能的执行次数,而是通过条件判断是否继续执行循环体,采用while语句实现。
for循环用于遍历结构(例如列表、元组、字符串或字典)中的元素,并对每个元素执行一段代码。它的语法格式如下:
for语句的循环执行次数是根据遍历结构中元素个数确定的。遍历循环从遍历结构中逐个提取元素,赋值给循环变量,对于所提取的每个元素执行一次语句块。
遍历结构可以是字符串、列表、文件或range()函数等,常使用的方式如下所示:
实例3.6展示了for循环的基本使用方法,该实例每隔10步检查一次电池电压。
遍历循环还有一种扩展模式,基本语法格式如下:
在扩展模式中,for...else语句用于在循环结束后执行一段代码。当循环执行完毕(即遍历完遍历结构中的所有元素)后,会执行else子句中的代码。如果在循环过程中遇到了break语句,则会中断循环,此时不会执行else子句。
实例3.7展示了for...else语句的基本使用,更多用法将在3.3.3节结合continue和break关键字进一步讲解。
实例3.7的输出结果如下:
很多程序在执行之初无法确定遍历结构,需要根据条件来判断是否执行循环,称为条件循环。Python通过while实现条件循环,基本语法格式如下:
当条件为True时,while语句循环体重复执行语句块中的语句;当条件为False时,循环终止,执行与while同级别的后续语句。当把条件始终设置为True时,将开始无限循环。
实例3.8展示了while语句的基本使用方法,该实例每隔10步检查一次电池电压,功能与实例3.6相同。
条件循环也有一种使用保留字else的扩展模型,基本语法格式如下:
在扩展模式中,当while循环执行完毕时,程序会执行else子句中的代码;如果在循环过程中遇到了break语句,则会中断循环,此时不会执行else子句。通常,在语句块2中放置判断循环执行情况的语句。
实例3.9展示了while循环中扩展模式的基本使用方法。
实例3.9输出结果为:
break和continue是Python中两个常用的关键字,用于控制循环结构的执行流程。break关键字用于立即终止当前循环,跳出for和while的循环体。它常用于在循环中查找特定元素,一旦找到目标元素就立即结束循环。continue关键字用于跳过当前循环的剩余部分,直接进入下一次循环,而不终止循环。它常用于在循环中忽略特定条件的元素。遍历循环和条件循环中插入break和continue关键字后,代码执行过程如图3.6所示。需要注意的是,如果存在多层循环结构,break语句在内层循环体中,那么break语句只跳出最内层循环,仍然继续执行外层循环,即每个break只有跳出当前循环的能力。
图3.6 break和continue对代码执行的影响
for循环和while循环都存在一个else扩展模式。else语句块只有在循环正常结束时才会被执行。一旦从for或while循环中终止,任何对应的else语句块都将不会被执行,如实例3.10和实例3.11对比所示。
实例3.10和实例3.11的输出结果分别为:
在Python中,异常处理是一种用于处理程序运行过程中出现的错误和异常情况的机制。它允许程序在遇到错误时采取适当的措施,而不是直接崩溃,使得程序运行更加稳定。在日常学习和工作中,一种行为可能会产生多种结果,其中的某些结果可能是超出预期的,但是我们要为这些超出预期的结果做好备用方案。
比如准备考研的同学,可能成功,也可能失败,考研失败能不能直接崩溃呢?从合理的状态来看是必须接受并正常面对的,这就需要在准备考研之前就想好失败的预案。人生中面对任何失意,我们都应从容面对。
Python提供了多种方法来处理异常,其中最常用的是try...except语句。它允许程序员将可能出现异常的代码放在try块中,然后在except块中捕获并处理异常,如实例3.12所示。
在上面的代码中,我们尝试将1除以0,这会导致ZeroDivisionError异常。我们使用try...except语句来捕获这个异常,并在except块中输出一条错误信息。
除了try...except语句外,Python还提供了其他几种方法来处理异常,包括else子句、finally子句和assert语句,如实例3.13所示。
在实例3.13中,使用try...except语句来捕获除零异常,并在except块中输出一条错误信息。由于发生了除零异常,因此不会执行else子句中的代码。最后,无论是否发生异常,都会执行finally子句中的代码。
异常处理在程序开发中具有重要意义,它能够帮助程序员更好地控制程序的运行流程,并在遇到错误时采取适当的措施。
random库是Python的内置模块,用于生成伪随机数。这些伪随机数并不是真正的随机数,而是通过特定算法生成的,random库采用梅森旋转算法生成伪随机数序列。该模块可以用来执行随机操作,例如生成随机数、从列表或字符串中打印随机值等。
random库提供了许多用于处理各种分布的伪随机数生成器。对于整数,有从指定范围内均匀选择的功能。对于序列,有从序列中均匀选择随机元素、就地生成列表随机排列以及无放回随机抽样的函数。对于浮点数,有计算均匀分布、正态(高斯)分布、对数正态分布、负指数分布、伽玛分布和贝塔分布的函数。对于角度,冯·米塞斯分布(von Mises分布)可用。
几乎所有的模块函数都依赖于基本函数random(),它在半开区间0.0≤ X <1.0内均匀生成一个随机浮点数。Python使用梅森旋转算法作为核心生成器。它产生53位精度的浮点数,并且具有2 19937 -1的周期。底层C实现既快速又线程安全。梅森旋转算法是最广泛测试过的随机数生成器之一。然而,由于完全确定性,它并不适用于所有目的,完全不适用于加密目的。
random库提供的函数实际上是隐藏的random.Random类实例的绑定方法。random模块还提供了SystemRandom类,它使用系统函数os.urandom()从操作系统提供的源生成随机数。
random库是Python内置模块,不需要安装即可直接使用。引用random库有以下两种方式:
random库中9个随机数生成函数见表3.2。
表3.2 random中随机数生成函数
生成随机数之前可以通过seed()函数指定随机数种子。随机数种子一般是一个整数,只要种子相同,每次生成的随机数序列也相同。这种情况便于测试和同步数据。
实例3.14展示了random库的基本使用方式,使用random实现了从参赛学校队伍列表随机找出两个学校的队伍使用B场地进行比赛。
Python的turtle库是一个提供给程序员虚拟画布来创建形状和图像的库,是Python语言的标准库之一,无须安装即可直接使用。turtle库使用一个名为turtle的屏幕画笔绘制形状和图像,该库的名称源自这支笔的名称。
turtle库最初是作为一种教育工具而创建的,供教师在课堂上使用。对于需要生成一些图形输出的程序员来说,它可以在不引入更复杂或外部库的情况下实现这一目标。它可以提供即时、可见的反馈,还提供了方便访问图形输出的通用方法,这些有效且经过验证的方法,能够让学习者接触编程概念并与软件进行交互。
turtle库以面向对象和面向过程的方式提供了turtle图形原语。在Python中,turtle图形提供了一个物理“海龟”的表示(带有笔的小型机器人),它从窗体正中心(0,0)开始在画布上游走,走过的轨迹形成了绘制的图形。“海龟”由程序控制,可以变化颜色、改变宽度等。通过简单移动重复的程序,Turtle可以绘制复杂的形状。
引用turtle库有以下两种方式:
turtle库中设置画布大小的函数见表3.3。
表3.3 turtle库中设置画布大小的函数
turtle库中设置画笔属性的函数见表3.4。
表3.4 turtle库中设置画笔属性的函数
turtle库中控制画笔的函数见表3.5。
表3.5 turtle库中控制画笔的函数
turtle库中画笔运动的函数见表3.6。
表3.6 turtle库中画笔运动的函数
turtle库中全局控制的函数见表3.7。
表3.7 turtle库中全局控制的函数
除上述列出的函数之外,turtle库还有一些不常用的绘图函数,有兴趣的读者可以深入探究。
(1)turtle库坐标系说明
在turtle库中,绘图窗体内部形成了一个空间坐标体系,包括绝对坐标和海龟坐标两种。对于绝对坐标,海龟最开始在画布的正中心,正中心的坐标就是(0,0)。海龟的运行方向向着画布的右侧,所以整个窗体的右方向是 X 轴,上方向是 Y 轴,由此构成了一个绝对坐标系。在绝对坐标系中,可以使用goto(x, y)函数来控制海龟移动到指定的坐标位置。例如,可以使用goto(100, 100)来让海龟移动到第一象限。
另一种坐标系是海龟坐标系。在海龟坐标系中,海龟的当前位置是原点(0,0),海龟的当前朝向是 X 轴正方向。可以使用setheading(angle)函数来控制海龟的朝向,其中angle是以度为单位的角度值。例如,可以使用setheading(90)来使海龟朝向 Y 轴正方向。在海龟坐标系中,可以使用forward(distance)和backward(distance)函数来控制海龟沿着当前朝向移动指定的距离。例如,可以使用forward(100)来让海龟向前移动100个单位。
(2)RGB颜色空间
RGB颜色空间是一种基于RGB颜色模型的彩色空间。RGB颜色空间通常用来描述电视屏幕和计算机显示器等显示设备的输入信号。在turtle库中,可以使用RGB颜色来设置画布和画笔的颜色。
RGB指红蓝绿三个通道的颜色组合,覆盖视力所能感知的所有颜色。RGB每色取值范围为0~255(整数)或0~1(小数)。常用RGB色彩见表3.8。
表3.8 常用RGB色彩对照表
实例3.15是一个简单的程序,它使用turtle库绘制一个正方形。
此外,还可以使用循环和条件语句来创建更复杂的图形和图案。
图3.7所示为全国大学生智能汽车竞赛百度创意组赛道。全国大学生智能汽车竞赛都是在封闭赛道上进行的,赛道直线段和曲线段相互衔接,首尾相连,在场地上还设计有城池、宿营地等场景。请用turtle库设计一个封闭的智能车比赛赛道,要求赛道能更好地验证赛车的转弯能力和加速性能等。
图3.7 全国大学生智能汽车竞赛百度创意组赛道
绘制完成的赛道如图3.8所示。
图3.8 绘制完成的赛道
Python的turtle库以一种有趣且互动的方式帮助程序员感受使用Python编程,turtle库还有一些有趣的功能,感兴趣的读者可自行探索。
一、选择题
1.( )给出的保留字不直接用于表示分支结构。
A.else
B.if
C.in
D.elif
2.以下关于Python语言中“缩进”说法正确的是( )。
A.缩进在同一个子语句块中长度统一且强制使用
B.缩进统一为4个空格
C.缩进是非强制的,仅为了提高代码可读性
D.缩进可以用在任何语句之后
3.在try...except结构中,finally块中的代码会在( )情况下执行。
A.只有try块中的代码正常执行完成
B.只有except块中的代码执行
C.无论是否执行except块
D.只有在except块中没有执行时才执行
4.下面( )语句用于在循环中跳过当前迭代,进入下一次迭代。
A.break
B.skip
C.continue
D.pass
5.Random库中,random.randint(1, 10)的作用是( )。
A.生成一个随机整数,范围在[1, 10]
B.生成一个随机小数,范围在[1, 10]
C.生成一个随机整数,范围在(1, 10)
D.生成一个随机小数,范围在(1, 10)
6.下列( )不是Python的比较运算符。
A.==
B.!=
C.<=
D.><
7.在Python中,( )关键字用于结束当前循环,并跳到当前层循环结构之后的代码。
A.exit
B.stop
C.break
D.continue
8.range(3, 10, 2)的输出是( )。
A.[3, 5, 7, 9]
B.[3, 6, 9]
C.[2, 4, 6, 8]
D.[4, 6, 8]
9.下列语句中,( )用于放下画笔,绘制出轨迹。
A.turtle.trace()
B.turtle.pd()
C.turtle.draw()
D.turtle.done()
二、判断题
1.Python中,if语句用于循环操作。( )
2.在Python中,elif是else if的缩写,用于处理多个条件。( )
3.Python中的程序流程图可以用菱形、圆形和矩形表示。( )
4.Python中的分支结构语句只有if语句。( )
5.Python中的random库可以生成随机数。( )
6.Python中的turtle库可以用来画图。( )
7.try-except块中的代码在没有异常发生时执行except块中的代码。( )
三、实训题
1.批量修改交通图像名称。智能车竞赛需要自行采集图像构建数据集,采集图像时分多次采集,每次采集的图像保存在一个文件夹中,命名从1开始,构建数据集时需要将多个文件夹中的图像合成到一个文件夹,因此需要批量修改文件名以避免同名文件覆盖。请设计一段程序,实现批量修改文件名的功能。假设原本有两个图像,图像名分别为1.jpg和2.jpg,修改为vehicle1.jpg和vehicle2.jpg。
2.正交编码器旋转状态解码。正交编码器是一种用于测量旋转速度和方向的传感器。最常见的正交编码器有两个输出信号:A相和B相。一般情况下,A相和B相的输出信号总是有π/2的相位差。编码器在旋转时,两条数据线上的电平信号不断变化,根据不同的变化状态可以计算出编码器旋转的方向。详细情形如下表所描述:
表中“+1”表示顺时针旋转一格,“-1”表示逆时针旋转一格。两相数据都发生变化时,无法判定是顺时针旋转还是逆时针旋转,于是都假定变化后的数据是在A相跳变时采集的数据。当采样频率足够高时,两相数据同时发生变化的概率很小,这样的计算方式是足够精确的。
根据上表中的内容,编写一段程序,要求分别输入变化前后A相和B相的值,输出编码器的旋转状态。