百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

后端编程Python3-控制结构(python中控制结构)

off999 2024-10-19 07:14 12 浏览 0 评论

本节是第五讲的第九小节,上节为大家介绍了Python语言组合数据类型的迭代与复制,本节将为大家介绍Python语言的控制结构。

控制结构(Control Structures)

Python通过if语句实现了条件分支,通过while语句与for...in语句实现了循环。Python中还有一种条件表达式是一种if语句,也是Python对C风格语言中使用的三元算子(?:)相对应的内容。

条件分支(Conditional Branching)

下面给出的是Python条件分支语句的最通常的语法:

if boolean_expression1:

suite1

elif boolean_expression2:

suite2

elif boolean_expressionN:

suiteN

else:

else_suite

可以有0个或多个elif语句,最后一个elif语句是可选的。如果我们需要考虑某个特定情况,但在该情况出现时又不需要做什么,那么可以使用pass (充当“什么也不做"占位符)作为该分支的suite。

有些情况下,可以将一条if...else语句缩减为单一的条件表达式,条件表达式的语法是:

expression1 if boolean_expression else expression2 #如果boolean_expression为True,条件表达式的结果为expression1,否则为expression2。

通常的程序设计模式是将某变量设置为默认值,在必要的时候再改变该值,比如, 由于用户的请求进行改变,或者针对程序当前运行平台进行改变。下面给出的是使用 if语句的常规模式:

offset = 20

if not sys.platform.startswith("win"):

offset = 10

sys.platform变量存放的是当前平台的名称,比如,“Win32”或“linux2”。使用条件表达式,可以通过一行代码完成上面的功能:

offset = 20 if sys.platform.startswith("win") else 10

这里并不需要使用圆括号,但是使用圆括号可以避免微妙的陷阱,比如,假定我们想将width变量设置为100,并在margin为True时额外加10,我们可能会使用如下的代码:

width = 100 + 10 if margin else 0 # WRONG!

特别讨厌的地方在于,如果margin为True,上面的代码可以正确工作,并将width 设置为110;如果margin为False,就会错误地将width设置为0,而非100,这是因为,Python将100 + 10看做条件表达式的expression1部分,解决这一问题的方法是使用圆括号:

width = 100 + (10 if margin else 0)

圆括号还可以使得代码更清晰,更适合阅读。

条件分支可用于提高为用户打印的消息,比如,在报告处理的文件数量时,不再打印“0 file(s)”、“1 file(s)”,而是使用一对条件表达式:

print("{0} file{1}".format((count if count != 0 else "no"),("s” if count != 1 else "")))

上面的语句将打印“no files”、“1 file”、“2 files",并且同时会留下一个更专业的印象。

循环(Looping)

Python提供了 while循环与for ... in循环,这两种循环方式实际上都具有比第1章中所展示的基本语法更复杂的语法格式。

while 循环(while Loops)

下面给出的是while循环完整的、通常的语法格式:

while boolean_expression:

while_suite

else:

else_suite

else分支是可选的。只要boolean_expression为True, while块的suite就会执行。 如果boolean_expression为False或变为False,循环就会终止,此时,如果存在可选的else分支,就会执行其suite。在while块的suite内部,如果执行了continue语句, 就会跳转到循环起始处,并对boolean_expression的取值进行重新评估。如果循环不能正常终止,就会跳过所有可选的else分支。

可选的else分支这种叫法很容易让人困惑,因为只要循环是正常终止的,else分支的suite就总会执行。如果由于break语句、或由于返回语句(如果循环在函数或方法内)、或由于发生异常导致跳出循环,else分支的suite就不会执行。(发生异常时, Python会跳过else分支并寻找适当的异常处理部分。)另一方面,else分支的这些特点对while循环、for ...in循环以及try ... except块都是一样的。

让我们看一下else分支的实际使用。str.index()与list.index()方法可以返回给定字符串或数据项的索引位置,并在找不到时产生ValueError异常。str.find()方法完成同样的功能,但在找不到时不会产生异常,而是返回索引值-1,对于列表,没有等价的方法,但如果需要一个完成这一功能的函数,可以使用while循环创建:

def list_find(lst, target):

index = 0

while index < len(lst):

if lst[index] == target:

break

index += 1

else:

index = -1

return index

这一函数在给定的列表中搜索目标。如果找到了目标,就使用break语句终止循环,并返回适当的索引位置。如果找不到目标,就会循环完毕并正常终止,之后执行else的suite:将索引位置设置为-1,并返回该值。

for循环(for Loops)

与while循环类似,for... in循环的完整语法也包括else分支:

for expression in iterable:

for_suite

else:

else_suite

通常,expression或者是一个单独的变量,或者是一个变量序列,一般是以元组形式给出的。如果将元组或列表用于expression,则其中的每一数据项都会拆分到表达式的项。

如果在for... in循环suite中执行了continue语句,那么控制流立即跳转到循环起始处,并开始下一次迭代。如果循环正常执行完毕,就会终止循环,之后执行else suite。如果循环被跳出(由于执行了break语句或return语句),控制流会立即跳转到循环后的语句——所有可选的else suite将被跳过。同样地,如果发生异常,Python会跳过else 分支并寻找适当的异常处理程序。

下面给出了list_find()的for ... in循环实现版本,与while循环实现的版本类似, 这一版本也展示了 else分支的实际应用:

def list_find(lst, target):

for index, x in enumerate(lst):

if x == target:

break

else:

index = -1

return index

如上面代码段所示,在for ... in循环的表达式中创建的变量在循环终止后仍然存在。与所有局部变量类似,他们也将在其闭合范围结尾处终止存在。

异常处理(Exception Handling)

Python通过产生异常来指明发生错误或异常条件——尽管有些第三方Python库使用更过时的技术,比如“error”返回值。

捕获与产生异常(Catching and Raising Exceptions)

异常的捕获是使用try ...except 块实现的,其通常语法如下:

try:

try_suite

except exception_group1 as variable1:

except_suite1

......

except exception_groupN as variableN:

except_suiteN

else:

else_suite

finally:

finally_suite

其中至少要包含一个except块,但else与finally块都是可选的。在try块的suite 正常执行完毕时,会执行else块的suite—如果发生异常,就不会执行。如果存在一 个finally块,则最后总会执行。

每个except分支的异常组可以是一个单独的异常,也可以是包含在括号中的异常元组。对每个异常组,as variable部分是可选的。如果使用,该变量就会包含发生的异常,并可以在异常块的suite中进行存取。

如果某个异常发生在try块的suite中,那么每个except分支会顺序尝试执行。如果该异常与某个异常组匹配,则相应的suite得以执行。要与异常组进行匹配,异常必须与组中列出的异常类型(或其中的某一个)一致,或者与组中列出的异常类型(或其中的某一个)的子类一致。

比如,如果在字典查询中产生KeyError异常,那么包含Exception类的第一个 except分支将匹配这一异常,因为KeyError是Exception的(非直接的)子类。如果没有哪个组列出了 Exception (通常是这种情况),但是存在一个LookupError,那么 KeyError异常也可以匹配,因为KeyError是LookupError的子类。如果没有哪个组列 出了 Exception或LookupError,但是某个组列出了 KeyError,就将匹配该组。

下图展示了从异常体系中抽取的部分关系图:

#下面给出的是不正确使用的一个实例:

try:

x = d[5]

except LookupError: # WRONG ORDER

print("Lookup error occurred")

except KeyError:

print("lnvalid key used")

如果字典d不包含key为5的数据项,那么我们希望产生最具针对性的异常 KeyError,而不是最通常的异常LookupError,但是这里,KeyError except块代码总是无法执行到。如果产生KeyError异常,就会与LookupError except块匹配,因为 LookupError是KeyError的一个基类,也就是说,在异常体系中,LookupError要高于 KeyError,因此,在使用多个except块时,我们必须坚持对其排序,从最具针对性的 (在异常体系中最底层)异常到最通常(异常体系中最顶层)的异常。

try:

x = d[k / n]

except Exception: # BAD PRACTICE

print("Something happened")

要注意的是,上面这种使用except Exception的方法通常并不是一种好做法,因为这种做法将捕捉所有异常,从而很容易掩盖代码中的逻辑错误。在上面的实例中,程序的本意是捕捉KeyErrors,但是如果n为0,就会无意间并且寂静地捕获一个 ZeroDivisionError 异常。

直接写成except:也是可能的,也就是说,不设置异常组。写成这种风格的异常块将捕获任意异常,包括那些继承自BaseException而非Exception的异常,这种做法会导致与使用except Exception同样的问题,甚至更严重,通常情况下应该总是避免这种做法。

如果没有哪个except块匹配该异常,Python会沿着调用栈回溯,并寻找适当的异常处理程序。如果找不到合适的异常处理程序,程序将终止,并在控制台上打印该异常以及回溯信息。

如果没有异常产生,那么任意可选的else块都将执行。在所有情况下——也就是说,如果没有发生异常,或发生异常并被处理,或发生异常并回溯到调用栈——任意 finally块的suite总是会得以执行。如果没有异常产生,或产生的异常被某个except 块处理,那么finally块的suite将在最后执行;如果异常产生,但是没有匹配的except 块,就首先执行finally块的suite,之后将该异常在调用栈中回溯。在需要确保资源被正确释放时,这种执行机制是很有用的。下图勾勒了通常的try ... except... finally语句块控制流。

#下面给出list_find()函数的最终版本,使用了异常处理:

def list_find(lst, target):

try:

index = Ist.index(target)

except ValueError:

index = -1

return index

上面的代码中,有效地使用了 try... except块,将异常转换为错误值。也可以使用同样的方法捕捉某种异常并产生另一种异常——稍后将讨论这一技术。

Python还提供了更简单的try ... finally块,有时也是有用的:

try:

try_suite

finally:

finally_suite

不管try块的suite中发生什么(当然,计算机或程序崩溃除外),finally块的suite 都将得以执行。将with语句与上下文管理器一起使用,也可以达到try ... finally块的效果。

try ... except... finally块的一种常见应用是处理文件错误。比如,noblanks.py程序 从命令行读取一个文件名列表,对其中每个文件,生成一个同名文件,但是后缀名改为.nb,内容上则去除原有文件中的空白行。下面给出的是该程序的read_data()函数:

def read_data(filename):

lines =[]

fh = None

try:

fh = open(filename, encoding="utf8")

for line in fh:

if line.strip():

lines.append(line)

except (lOError, OSError) as err:

print(err)

return []

finally:

if fh is not None:

fh.close()

return lines

上面的代码中,我们将文件对象fh设置为None,因为open。调用可能失败,这种情况下不会为fh赋值(因而fh保持为None),并产生一个异常。如果产生我们指定的某个异常(lOError或OSError),在打印错误消息后,就会返回一个空列表。需要注意的是,在返回之前,finally块的suite将得以执行,因此文件将安全地关闭—— 如果此前该文件被成功打开。

还要注意的是,如果发生编码错误,即便我们并不捕捉相关的异常(ValueError), 文件仍然会安全地关闭。这种情况下,finally块的suite将得以执行,之后异常被向上传递到调用栈——此时没有返回值,因为函数是由于未处理的异常导致结束的。此外, 由于没有适当的except块捕捉编码错误异常,因此程序将终止并打印回溯信息。

我们可以将except分支写的稍详细一些:

except EnvironmentError as err:

print(err)

return []

上面的代码可以正常工作,因为EnvironmentError是lOError与OSError的基类。 在后面,我们将展示一个稍紧凑一些的惯用法,以确保文件被安全关闭,而不需要finally块。

产生异常(Raising Exceptions)

异常提供了一种改变控制流的有用方法。我们可以使用内置的异常,或创建自己的异常,以便产生我们所需要的异常并对其进行处理。有两种可以产生异常的语法:

raise exception(args)

raise exception(args) from original_exception

raise

使用第一种语法时,指定的异常应该或者是内置的异常,或者继承自Exception 的自定义异常。如果给定一些文本作为该异常的参数,那么在捕捉到该异常并打印时, 这些文本应该为输出信息。使用第二种语法,也就是没有指定异常时,raise将重新产生当前活跃的异常——如果当前没有,就会产生一个TypeError。

自定义异常(Custom Exceptions)

自定义异常是自定义的数据类型(类)。创建类在后面讲述,不过创建简单的自定义异常类型比较简单,这里我们展示其基本的语法:

class exceptionName(baseException): pass #基类应该为Exception类或继承自Exception的类。

found = False

for row, record in enumerate(table):

for column, field in enumerate(record):

for index, item in enumerate(field):

if item == target:

found = True

break

if found:

break

if found:

break

if found:

print("found at ({0}, {1}, {2})".format(row, column, index))

else:

print("not found")

上面这15行代码是复杂的,因为我们必须分别跳出每个循环。一种替代的解决方法是使用自定义异常:

class FoundException(Exception): pass

try:

for row, record in enumerate(table):

for column, field in enumerate(record):

for index, item in enumerate(field):

if item == target:

raise FoundException()

except FoundException:

print("found at ({0}, {1}, {2})".format(row, column, index))

else:

print("not found")

这种方法可以将代码削减到10行,或者11行(包含异常定义本身),并且更易于理解。如果发现了要寻找的数据项,就产生自定义的异常,并执行except块的suite, else块则被跳过。如果没有找到相应的数据项,就不会产生异常,并在最后执行else suite。

以上内容部分摘自视频课程05后端编程Python-9控制结构,更多实操示例请参照视频讲解。跟着张员外讲编程,学习更轻松,不花钱还能学习真本领。

相关推荐

每天一个 Python 库:datetime 模块全攻略,时间操作太丝滑!

在日常开发中,时间处理是绕不开的一块,比如:生成时间戳比较两个时间差转换为可读格式接口传参/前端展示/日志记录今天我们就用一个案例+代码+思维导图,带你完全搞定datetime模块的用法!...

字节跳动!2023全套Python入门笔记合集

学完python出来,已经工作3年啦,最近有很多小伙伴问我,学习python有什么用其实能做的有很多可以提高工作效率增强逻辑思维还能做爬虫网站数据分析等等!!最近也是整理了很多适合零基...

为什么你觉得Matplotlib用起来困难?因为你还没看过这个思维导图

前言Matplotlib是一个流行的Python库,可以很容易地用于创建数据可视化。然而,设置数据、参数、图形和绘图在每次执行新项目时都可能变得非常混乱和繁琐。而且由于应用不同,我们不知道选择哪一个图...

Python新手必看!30分钟搞懂break/continue(附5个实战案例)

一、跳转语句的使命当程序需要提前结束循环或跳过特定迭代时,break和continue就是你的代码急刹按钮和跳步指令。就像在迷宫探险中:break=发现出口立即离开continue=跳过陷阱继续前进二...

刘心向学(24)Python中的数据类(python中5种简单的数据类型)

分享兴趣,传播快乐,增长见闻,留下美好!亲爱的您,这里是LearningYard新学苑。今天小编为大家带来文章“刘心向学(24)Python中的数据类”欢迎您的访问。Shareinterest,...

刘心向学(25)Python中的虚拟环境(python虚拟环境安装和配置)

分享兴趣,传播快乐,增长见闻,留下美好!亲爱的您,这里是LearningYard新学苑。今天小编为大家带来文章“刘心向学(25)Python中的虚拟环境”欢迎您的访问。Shareinte...

栋察宇宙(八):Python 中的 wordcloud 库学习介绍

分享乐趣,传播快乐,增长见识,留下美好。亲爱的您,这里是LearingYard学苑!今天小编为大家带来“Python中的wordcloud库学习介绍”欢迎您的访问!Sharethefun,...

AI在用|ChatGPT、Claude 3助攻,1分钟GET高颜值思维导图

机器之能报道编辑:Cardinal以大模型、AIGC为代表的人工智能浪潮已经在悄然改变着我们生活及工作方式,但绝大部分人依然不知道该如何使用。因此,我们推出了「AI在用」专栏,通过直观、有趣且简洁的人...

使用DeepSeek + Python开发AI思维导图应用,非常强!

最近基于Deepseek+PythonWeb技术开发了一个AI对话自动生成思维导图的应用,用来展示下如何基于低门槛的Python相关技术栈,高效结合deepseek实现从应用场景到实际应用的快速落地...

10幅思维导图告诉你 - Python 核心知识体系

首先,按顺序依次展示了以下内容的一系列思维导图:基础知识,数据类型(数字,字符串,列表,元组,字典,集合),条件&循环,文件对象,错误&异常,函数,模块,面向对象编程;接着,结合这些思维导图主要参考的...

Python基础核心思维导图,让你轻松入门

Python基础核心思维导图【高清图文末获取】学习路线图就给大家看到这里了,需要的小伙伴下方获取获取方式看下方图片...

Python基础核心思维导图,学会事半功倍

Python基础核心思维导图【高清图文末获取】学习路线图就给大家看到这里了,需要的小伙伴下方获取获取方式看下方图片...

硬核!288页Python核心知识笔记(附思维导图,建议收藏)

今天就给大家分享一份288页Python核心知识笔记,相较于部分朋友乱糟糟的笔记,这份笔记更够系统地总结相关知识,巩固Python知识体系。文末获取完整版PDF该笔记学习思维导图:目录内容展示【领取方...

Python学习知识思维导图(高效学习)

Python学习知识思维导图python基础知识python数据类型条件循环列表元组字典集合字符串序列函数面向对象编程模块错误异常文件对象#python##python自学##编程#...

别找了!288页Python核心知识笔记(附思维导图,建议收藏)

今天就给大家分享一份288页Python核心知识笔记,相较于部分朋友乱糟糟的笔记,这份笔记更够系统地总结相关知识,巩固Python知识体系。文末获取完整版PDF该笔记学习思维导图:目录内容展示【领取方...

取消回复欢迎 发表评论: