异常:什么是异常呢?就好比你每天骑车上下班,突然有一天路上有个坑,你摔倒了,这其实就是异常,那应该怎么解决呢?我们只需要在坑那个地方放一块醒目的牌子,那你以后到那里,就不会摔倒了,当然这知识生活中的异常,代码里面的异常指的是程序在执行过程中出现了未知的错误,而这个错误是可以用其他代码进行修复的,比如X/Y;当Y为0的时候就会有异常,那我们只需要判断当Y不为0在进行X/Y就可以了;
常见的系统异常:常见的系统异常很多,1.除零异常:zerodevisionError;2.名称异常:nameError;3.索引异常:indexError;4.键异常:keyError;5值异常:valueError;6.属性异常:attributeError;7.迭代器异常:stopIterration;这么多异常其实都是继承自Exception,Exception又继承自BaseException,如果我们想要自定义异常就需要把类继承Exception;
解决异常:异常的解决,我们主要分为三个方面,一是进行容错处理,不让它抛出异常,但是如果容错代码偏多,就会造成代码的混乱,核心代码不清晰;
二:就是把异常捕获到,进行处理,我们可以看下面的代码,捕获异常用的是try except,首先把可能会产生异常的代码块放在try里面,然后针对可能会产生的异常放在except后,针对不同的异常会执行不同的except,还可以用as把异常信息打印出来,针对try里面的语句,只要产生了异常就会去执行except里面的代码块,不会挨个捕捉所有异常,如果没有产生异常则执行else语句,finally是不管怎么样都要执行的代码块;
捕捉异常的注意事项:1.当多个异常的处理方式相同时,我们可以把异常写在一个元组里面2.当我们不知道是什么异常时,可以直接写exception;
三:就是用with语句,with语句的作用就是在执行某一段代码之前进行预处理,这段代码执行结束后进行清理操作,最重要的一点就是这段代码不管出现什么异常,最终都要执行清理操作,比如说我们对一个文件进行读写操作,主要分为3步,首先是打开文件,然后读写文件,最后是关闭文件,对于关闭文件这里就是无论读写操作有没有发生异常都要执行的,用with语句就很方便,当然用try,except也可以,但是with语句更便捷,那我们当然首选with语句了;
with语句的原理分析:我们先看with语句的语法:
with context_expression as context:
代码块
如上所示,就是with语句的语法,其中context_expression是一个上下文表达式,这个表达式会返回一个上下文管理器,所谓上下文管理器就是实现了上下文协议的对象,上下文协议是指实现了__enter__和__exit__两个方法;它的调用顺序就是先执行 context_expression得到上下文管理器,然后执行上下文管理器的__enter__方法,并且如果写了 as context语句就会把__enter__方法的返回值赋值给context,然后执行代码块,最后执行上下文管理器的__exit__方法,需要注意的就是不管代码块有没有发生异常,__exit__方法都会被执行;
举个例子:比如读写数据库,我们不管读写操作怎么样,我们都必须要执行的两个步骤就是打开文件和关闭文件
用 try except:
try:
file=open("write.png",'rb')
readcontent=file.read()
except Exception as e:
print(e)
finally:
file.close()
用with语句:
with open("write.png",'rb') as f:
readcontent=f.read()
我们可以看出用with语句就是这么简单,我们只需要关注的就是我们的业务层,其实这里的open()就是一个上下文表达式,它已经实现了__enter__和__exit__,并把打开和关闭文件的操作封装了起来;在python里面还有很多类式的上下文表达式;
自定义上下文管理器:我们知道上下文管理器就是实现了__enter__和__exit__两个方法,那我们肯定就必须要实现这两个方法,下面的ContextManger就是一个上下文管理器了,我们再看代码的执行,首先执行了__enter__方法,__enter__方法的返回值传给了cm,在执行代码块,最后执行__exit__方法,这个方法有四个参数,我们主要看后面三个参数的意义,通过打印我们得知:exc_type是异常类型,exc_val是异常的值,exc_tb是异常的追踪信息,我们可以通过traceback的extract_tb拿到追踪信息,还有一点就是__exit__如果返回了true则异常就被捕捉到自己处理了,如果返回false就会把异常抛出去,抛给外界;
contextlib模块:上面我们自定义了上下文管理器,主要就是实现__enter__和__exit__,那有没有更简单的方法呢?答案是有的,就是用contexlib模块,现在我们来看用contextlib模块怎么快速实现上下文管理器
1.@contextlib.contextmanger:使用装饰器把生成器变成一个上下文管理器:如下所示,关于生成器的概念,之前有讲过,这里就不再讲解了,生成器主要是有yield关键字,这里使用contextlib.contextmanger装饰器装饰之后,yield语句之前的代码块就相当于是__enter__,之后的语句就相当于是__exit__,yield后面跟的语句就相当于是__enter__的返回值;
应用举例:如下 就是生成器变成上下文管理器的应用实例,说实话,我第一次看到这样写的时候,很是震惊,因为这个语法太灵活了,没有固定的套路,关键是看自己怎么去运用;
2.contextlib.closing:把一个拥有close方法并且不是上下文管理器的对象变成上下文管理器:这种情况主要适用于执行了某个方法之后,必须要执行另一种方法,这个模块相当于把__enter__封装进去了,并且返回的是该实例对象,该对象的close方法就对应着__exit__方法:
上下文管理器的嵌套:在python2.7之前使用的是contextlib.nested(),由于已经过时了,这里就不在讲述了;
手动抛出异常:我们之前都讲的是捕获异常,那怎么抛出异常呢?用的是raise语句,什么情况下应该抛出异常呢?主要就用于自己写了一个模块,这个模块对传入的参数有严格的限制,这种情况当别人调用传入参数有问题的时候,就应该抛出异常,让调用者警觉;
自定义异常:我们知道所有的异常都是继承自BaseException,其实常见的异常是继承自Exception,Exception在继承自BaseException,所以我们自定义异常只需要继承自Exception就可以了:
异常这部分就结束了,针对异常,我们主要是需要知道怎么去捕获它,怎么去解决它,然后上下文管理器针对某一些场景提供了便捷也是需要掌握的,contextlib模块提供了快速构造上下文管理器的方法也很重要,总之熟能生巧,多用,多敲,慢慢的就能游刃有余了,喜欢的童鞋,点个赞,谢谢大家;
网友评论