找回密码
 会员注册
查看: 23|回复: 0

Python进阶语法:with...as

[复制链接]

2万

主题

0

回帖

7万

积分

超级版主

积分
71619
发表于 2024-9-9 23:49:17 | 显示全部楼层 |阅读模式
1.Pythonwith…as…是什么Python的with…as…语句,就像一个贴心的管家,负责照顾你的资源,让你不再担心忘记关闭文件、网络连接或数据库事务等。这个管家在你进入“房间”时自动打开门,离开时帮你把门关上,真的是非常贴心!这个管家的工作方式很简单,你只需要在with后面跟上你要管理的资源,然后在as后面给这个资源起个名字。一旦你进入了这个“房间”,这个资源就被自动管理了。1.1with…as…语法结构withexpression[astarget]:withbody12参数说明:expression:是一个需要执行的表达式;target:是一个变量或者元组,存储的是expression表达式执行返回的结果,[]表示该参数为可选参数。1.2with…as…用法示例例如,当你打开一个文件时,你可能会这样写:file=open("example.txt","r")#手动打开content=file.read()file.close()#手动关闭123但是这样写有个问题,如果代码在读取文件内容或关闭文件时出现异常,文件可能不会被正确关闭。这时,你就可以请出with…as…语句来帮忙:withopen("example.txt","r")asfile:content=file.read()#在这里你可以放心地使用file,不必担心忘记关闭它。123这样,无论在with块中的代码是否出现异常,文件都会在退出块时被自动关闭,你就不必再担心忘记关闭文件了。with…as…语句,在Python中叫做:上下文管理器,它在Python中实现了自动分配并释放资源。2.Pythonwith…as…工作原理2.1with…as…的由来with…as是python的控制流语句,像if,while一样。with…as语句是简化版的try…except…finally语句。先理解一下try…except…finally语句是干啥的。实际上try…except语句和try…finally语句是两种语句,用于不同的场景。但是当二者结合在一起时,可以“实现稳定性和灵活性更好的设计”。2.1.1try…except语句用于处理程序执行过程中的异常情况,比如语法错误、从未定义变量上取值等等,也就是一些python程序本身引发的异常、报错。比如你在python下面输入1/0:a=1/0print(a)#ZeroDivisionError:divisionbyzero123是的,上面的代码会引发一个ZeroDivisionError异常,因为它在尝试将1除以0。在Python中,任何数字除以0都是未定义的,所以会引发这个错误。如果你想避免这个错误,你可以使用try和except语句来捕获这个异常:#声明变量a,并赋值为1除以0,这会导致ZeroDivisionErrora=1/0#尝试执行接下来的代码块,如果发生异常,则执行except块try:#打印变量a的值,因为a=1/0,所以这里会引发ZeroDivisionErrorprint(a)#如果try块中的代码引发ZeroDivisionError异常,则执行此except块exceptZeroDivisionError:#当除以零时,打印一个错误消息print("Divisionbyzeroisnotallowed!")123456789101112这样,当尝试除以零时,程序不会崩溃,而是会输出一个错误消息。try…except的标准格式try:#normalblockexceptA:#excAblockexcept:#excotherblockelse:#noErrorblock12345678程序执行流程是:–>执行normalblock–>发现有A错误,执行excAblock(即处理异常)–>结束如果没有A错误呢?–>执行normalblock–>发现B错误,开始寻找匹配B的异常处理方法,发现A,跳过,发现exceptothers(即except,执行excotherblock–>结束如果没有错误呢?–>执行normalblock–>全程没有错误,跳入else执行noErrorblock–>结束我们发现,一旦跳入了某条except语句,就会执行相应的异常处理方法(block),执行完毕就会结束。不会再返回try的normalblock继续执行了。try:a=1/2#anormalnumber/variableprint(a)b=1/0#anabnormalnumber/variableprint(b)c=2/1#anormalnumber/variableprint(c)except:print("Error")123456789'运行运行结果是,先打出了一个0.5,又打出了一个Error。就是把ZeroDivisionError错误捕获了。先执行try后面这一堆语句,由上至下:step1:a正常,打印a.于是打印出0.5(python3.x以后都输出浮点数)step2:b,不正常了,0不能做除数,所以这是一个错误。直接跳到except报错去。于是打印了Error。step3:其实没有step3,因为程序结束了。c是在错误发生之后的b语句后才出现,根本轮不到执行它。也就看不到打印出的c了但这还不是try…except的所有用法,except后面还能跟表达式的。所谓的表达式,就是错误的定义。也就是说,我们可以捕捉一些我们想要捕捉的异常。而不是什么异常都报出来。异常分为两类:python标准异常自定义异常我们先抛开自定义异常(因为涉及到类的概念),看看except都能捕捉到哪些python标准异常。请查看:Python异常处理2.1.2try…finallly语句用于无论执行过程中有没有异常,都要执行清场工作。try:executionblock#正常执行模块exceptA:excAblock#发生A错误时执行exceptB:excBblock#发生B错误时执行excepttherblock#发生除了A,B错误以外的其他错误时执行else:ifnoexception,jumptohere#没有错误时执行finally:finalblock#总是执行123456789101112tips:注意顺序不能乱,否则会有语法错误。如果用else就必须有except,否则会有语法错误。try:a=1/2print(a)print(m)#抛出NameError异常,此后的语句都不在执行b=1/0print(b)c=2/1print(c)exceptNameError:print("Ops!!")#捕获到异常exceptZeroDivisionError:print("Wrongmath!!")except:print("Error")else:print("Noerror!yeah!")finally:#是否异常都执行该代码块print("Successfully!")123456789101112131415161718'运行运行代码分析:try块开始。a=1/2:这行代码是合法的,所以a被赋值为0.5。print(a):输出0.5。print(m):这行代码尝试打印变量m,但在此之前没有定义m,所以会抛出一个NameError异常。由于在try块中抛出了NameError异常,所以会跳到相应的except块。该块捕获到异常并输出“Ops!!”。由于异常已经被捕获,后面的代码(包括b=1/0、print(b)、c=2/1和print(c))都不会执行。跳过else块(因为有一个被捕获的异常)。进入finally块,并输出“Successfully!”。总结:当运行此代码时,输出将是:0.5Ops!!Successfully!123注意:尽管有b=1/0和c=2/1,但它们不会被执行或输出,因为前面的NameError异常已被捕获。说完上面两个概念,我们再来说说with…as…语句。with…as…是从Python2.5引入的一个新的语法,它是一种上下文管理协议,目的在于从流程图中把try,except和finally关键字和资源分配释放相关代码统统去掉,简化try...except...finlally的处理流程。2.2with…as…工作原理with…as…语句相当于下面这段代码:try:#尝试打开名为'example.txt'的文件,模式为'r',表示只读模式。f=open('example.txt','r')except:#如果在尝试打开文件时发生任何异常(例如文件不存在),则执行此块。print('failtoopen')#打印错误消息,告知用户无法打开文件。exit()#终止程序。else:#如果打开文件成功,则尝试读取文件内容并打印print(f.read())finally:#不论是否发生异常,最后都会执行此块。f.close()#关闭文件。这一步很重要,因为它确保文件资源被正确释放。1234567891011121314这是不是很麻烦,但正确的方法就是这么写。我们为什么要写finally,是因为防止程序抛出异常最后不能关闭文件,但是需要关闭文件有一个前提就是文件已经打开了。下面,我们从try…finally…语法结构谈起:setthingsuptry:dosomethingfinally:tearthingsdown12345这东西是个常见结构,比如文件打开,setthingsup就表示f=open('xxx'),tearthingsdown就表示f.close()。在比如像多线程锁,资源请求,最终都有一个释放的需求。Try…finally结构保证了tearthingsdown这一段永远都会执行,即使上面dosomething的工作没有完全执行。如果经常用这种结构,我们首先可以采取一个较为优雅的办法,封装!defcontrolled_execution(callback):setthingsuptry:callback(thing)finally:tearthingsdowndefmy_function(thing):dosomethingcontrolled_execution(my_function)1234567891011这段代码用于控制另一个函数的执行过程。下面是对代码的原理说明:定义controlled_execution函数:这个函数接受一个回调函数callback作为参数。执行setthingsup代码,通常用于设置或准备执行回调函数所需的环境或资源。使用try...finally结构确保无论回调函数执行是否成功,都能执行清理操作。在try块中,调用传入的回调函数callback(thing)并传入一个名为thing的参数。在finally块,执行tearthingsdown代码,用于清理准备阶段设置的环境或资源。定义my_function函数:这个函数简单地执行某些操作(具体操作没有给出),并且它接受一个名为thing的参数。最后,调用controlled_execution(my_function)执行整个流程。这意味着my_function会作为回调函数传递给controlled_execution,并由它来控制执行过程。这种模式通常用于封装和抽象执行细节,使得外部代码可以专注于其核心功能,而不必关心环境设置和清理等辅助任务。通过这种方式,可以更容易地管理和维护代码,特别是在处理可能引发异常的操作时。封装是一个支持代码重用的好办法,但是这个办法很dirty,特别是当dosomething中有修改一些localvariables(局部变量)的时候(变成函数调用,少不了带来变量作用域上的麻烦)。另一个办法是使用生成器,但是只需要生成一次数据,我们用for-in结构去调用它:defcontrolled_execution():#setthingsuptry:yieldthingfinally:#tearthingsdownforthingincontrolled_execution():#dosomethingwiththing123456789这段代码定义了一个生成器函数controlled_execution,并使用yield关键字在生成器函数中创建了一个可以返回值的迭代器。下面是对代码的原理说明:定义controlled_execution函数:这个函数是一个生成器函数,因为它使用了yield关键字。生成器函数与普通函数不同,它们返回一个迭代器,可以在多次调用之间记住其状态。执行setthingsup代码,通常用于设置或准备执行回调函数所需的环境或资源。使用try...finally结构来确保无论回调函数执行是否成功,都能执行清理操作。在try块中,使用yieldthing返回一个值给调用者,并将这个值存储在生成器对象中。此时,生成器函数的执行被暂停,等待下一次迭代请求。在finally块中,执行tearthingsdown代码,这通常用于清理在准备阶段设置的环境或资源。调用controlled_execution函数:通过使用for循环迭代调用controlled_execution()函数,可以逐个处理生成器返回的值。每次迭代都会从生成器中请求下一个值(使用next()函数),直到没有更多值可供请求。处理返回的值:在for循环的每次迭代中,都会执行dosomethingwiththing代码,使用从生成器中获取的值(即yieldthing返回的值)作为输入。这种生成器模式在需要控制资源访问或处理潜在异常的情况下非常有用。通过使用生成器,可以将复杂的资源管理逻辑与主要的业务逻辑分开,使代码更加清晰和易于维护。因为thing只有一个,所以yield语句只需要执行一次。当然,从代码可读性也就是优雅的角度来说这简直是糟糕透了。我们在确定for循环只执行一次的情况下依然使用了for循环,这代码给不知道的人看一定很难理解这里的循环是什么个道理。最终的python-dev团队的解决方案。(python2.5以后增加了with…as…表达式的语法)classcontrolled_execution:def__enter__(self):#setthingsupreturnthingdef__exit__(self,type,value,traceback):#tearthingsdownwithcontrolled_execution()asthing:#dosomething12345678910在这里,python使用了with-as的语法。当python执行这一句时,会调用__enter__函数,然后把该函数return的值传给as后指定的变量。之后,python会执行下面dosomething的语句块。最后不论在该语句块出现了什么异常,都会在离开时执行__exit__。另外,__exit__除了用于tearthingsdown,还可以进行异常的监控和处理,注意后几个参数。要跳过一个异常,只需要返回该函数True即可。下面的样例代码跳过了所有的TypeError,而让其他异常正常抛出。def__exit__(self,type,value,traceback):returnisinstance(value,TypeError)12'运行运行在python2.5及以后,file对象已经写好了__enter__和__exit__函数,我们可以这样测试:f=open('example.txt','r')print(f)#返回:print(f.__enter__())#返回:print(f.read())#返回:HELLOWORLD!print(f.__exit__())#返回:None123456789之后,我们如果要打开文件并保证最后关闭他,只需要这么做:withopen("x.txt")asf:data=f.read()#dosomethingwithdata1234如果有多个项,我们可以这么写:withopen("x.txt")asf1,open('xx.txt')asf2:#dosomethingwithf1,f2123上文说了__exit__函数可以进行部分异常的处理,如果我们不在这个函数中处理异常,他会正常抛出,这时候我们可以这样写(python2.7及以上版本,之前的版本参考使用contextlib.nested这个库函数):try:withopen("a.txt")asf:#dosomethingexceptxxxError:#dosomethingaboutexception123456789如果文件"x.txt"不存在,使用open("x.txt")会引发一个FileNotFoundError异常。为了处理这个异常,你可以使用try...except语句来捕获异常并执行相应的处理逻辑。下面是一个示例代码,展示了如何处理文件不存在的情况:try:withopen("x.txt")asf:data=f.read()print(data)exceptFileNotFoundError:print("文件不存在,请检查文件路径或文件名是否正确。")1234567891011'运行运行在上述代码中,我们使用try...except语句来捕获FileNotFoundError异常。如果文件不存在,会执行except块中的代码,打印出一条错误消息。这样,当文件不存在时,程序不会崩溃,而是会输出一个友好的错误提示,帮助用户了解问题所在。总之,with-as表达式极大的简化了每次写finally的工作,这对保持代码的优雅性是有极大帮助的。如果你觉得文章还不错,请大家点赞、分享、留言下,因为这将是我持续输出更多优质文章的最强动力!如果想要系统学习Python、Python问题咨询,或者考虑做一些工作以外的副业,都可以扫描二维码添加微信,围观朋友圈一起交流学习。我们还为大家准备了Python资料和副业项目合集,感兴趣的小伙伴快来找我领取一起交流学习哦!关于Python学习指南学好Python不论是就业还是做副业赚钱都不错,但要学会Python还是要有一个学习规划。最后给大家分享一份全套的Python学习资料,给那些想学习Python的小伙伴们一点帮助!包括:Python激活码+安装包、Pythonweb开发,Python爬虫,Python数据分析,人工智能、自动化办公等学习教程。带你从零基础系统性的学好Python!👉Python所有方向的学习路线👈Python所有方向路线就是把Python常用的技术点做整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。(全套教程文末领取)👉Python学习视频600合集👈观看零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。温馨提示:篇幅有限,已打包文件夹,获取方式在:文末👉Python70个实战练手案例&源码👈光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。👉Python大厂面试资料👈我们学习Python必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。👉Python副业兼职路线&方法👈学好Python不论是就业还是做副业赚钱都不错,但要学会兼职接单还是要有一个学习规划。👉这份完整版的Python全套学习资料已经上传,朋友们如果需要可以扫描下方CSDN官方认证二维码或者点击链接免费领取【保证100%免费】加粗样式
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2025-1-10 05:38 , Processed in 0.632323 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表