Part 2异常和错误
---恢复内容开始---
1.什么是异常:
异常就是在程序运行期时发生错误的信号,在python中,错误触发的异常如下:(在pycharm中如图所示,异常为列表下标越界)
2.异常种类:
在python中不同的异常可以用不同的类型去标识,不同的类对象标识不同的异常,一个异常标识一种错误。
比如一些常见错误如下
1 AttributeError: 试图访问一个对象没有的属性,比如foo.x,但是foo没有属性x 2 IOError :输入/输出异常;基本上是无法打开文件 3 ImportError: 无法引入模块或包;基本上是路径问题或名称错误 4 IndentationError: 语法错误(的子类) ;代码没有正确对齐 5 IndexError :下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5] 6 KeyError :试图访问字典里不存在的键 7 KeyboardInterrupt :Ctrl+C被按下,强制退出 8 NameError: 使用一个还未被赋予对象的变量 9 SyntaxError: Python代码非法,代码不能编译(个人认为这是语法错误,写错了) 10 TypeError :传入对象类型与要求的不符合 11 UnboundLocalError: 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,导致你以为正在访问它 12 ValueError: 传入一个调用者不期望的值,即使值的类型是正确的
3.异常处理:
如不进行异常处理,异常发生之后python解析器执行时就会终止,如要避免这种情况,就需要程序员自己编写捕捉这个异常的代码,捕捉成功后会进入捕捉后的代码块儿运行,而不会提前终止,从而增强了程序的健壮性和容错性。
处理方法是采用一种特定的语法格式用来进行异常处理:
- (1)基本语法格式:
try: 被检测的代码块 except: try中一旦检测到异常,就执行这个位置的逻辑
举个例子:
eg1:
try: list = [1,2,3] print(list[4]) except IndexError: print("下标越界")
- (2)异常类只能用来处理指定的异常情况,如果非指定则无法处理,所以可采用多分支结构:
try: list = [1,2,3] print(list[4]) except IndexError as e: print(e) except KeyError as e: print(e) except ValueError as e: print(e) 》》》输出: list index out of range
- (3)万能异常,Exception,他而已捕捉任何异常:
try: list = [1,2,3] print(list[4]) except Exception as e: print(e) 》》》输出: list index out of range
那么问题来了!是不是所有的我就用一个Exception就够啦,的确,大多数是可以这样,不过我们还是得看看下列两种情况:
(1)如果只想在异常出现后进行统一的丢弃或者使用同一种处理异常的方法,那没有关系,大胆用吧!
(2)如果针对不同的异常,我们有不同的解决措施,那我们就应该用多分支结构,for example:
s = 'abc' l = [1,2,3] try: int(s) #print(l[5]) 如果int(s)被注释掉,那么处理异常的就是第一个了 except IndexError: print("请重新确定输出的元素") except ValueError: print("不能操作")
except Exception:
print(“其他错误”) 》》》输出: 不能操作
通常情况下,我们会在最后把万能异常加上以防万一,但注意如果要加,只能在单分支异常后面!!
- (4)异常的其他结构:
s = 'abc' l = [1,2,3] try: int(s) #print(l[5]) except IndexError as e: print(e) else: print("没有指定的错误") finally: print("无论正常与否,都要执行该模块,通常是进行清理工作")
else 表示如果没有捕捉到异常,就执行else,也就是说else 和except 是互斥的,并且用到else的地方,必要except,反之不成立
finally 执行代码块内容,无论是否触发了错误都会被执行,并且只能在最后
通常是完成一些收尾工作,比如文件打开了却出现异常,那么就需要关闭文件!
即使try代码块儿里面有return语句也会执行finally块儿,finall就是这么强势!
举个例子吧,如果知道装饰器的小伙伴可以看看下例,如果没有,就。。。就记住上面结论并看看相应的博客吧!
import time #定义一个装饰器: def wrapper(func): def inner(*args, **kwargs): #捕获是否有异常: try: start = time.time() ret = func(*args, **kwargs) return ret #无论有不有最后都要执行下列语句块 finally: end = time.time() print(end - start) return inner @wrapper def func(): time.sleep(1) func() 》》》输出: 1.0016651153564453
在 return ret 之前会执行finally的代码;
在执行 finally 的代码后,再return。
- (5)主动触发异常:
语法格式为:
raise TypeError
干说无异,举例子,不过,接下来这个例子要用的面向对象的相关知识,所以还没了解到的同学又得自己去看看了。
#定义了支付父类,里面定义了pay()并自动引发错误 class Payment: def pay(self, money): raise NotImplementedError('没有实现方法') #接下来分别定义了两个支付子类继承父类,如果子类没有pay(),就会往父类寻找。 class Alipay(Payment): def pay(self, money): print("支付了%s元"&money) class WechatPay(Payment): def fuqian(self, money): print("支付了%s元"%money) #定义了一个方法,根据传入参数不同完成相同pay(),这里用到了多态的实现 def pay(obj, money): obj.pay(money) we = WechatPay() pay(we, 10) 》》》输出: NotImplementedError: 没有实现方法
分析:因为we是一个WechatPay的实例对象,WechatPay又继承了Payment,所以在子类没有pay方法的时候,就往父类找从而主动引发异常!
这里就要提问了:怎么样该段代码才不会出现异常呢?欢迎评论,笑嘻嘻。
- (6)自定义异常
这里就要知晓异常祖宗的祖宗:BaseException
1 class EvaException(BaseException): 2 def __init__(self, msg): 3 self.msg = msg 4 def __str__(self): 5 return self.msg 6 7 try: 8 raise EvaException('类型错误') 9 except EvaException as e: 10 print(e) 11 12 13 》》》输出: 14 NotImplementedError: 没有实现方法
4.总结:
总的来说,异常处理语段有利有弊,利在前面介绍了,就是避免了程序的突然死掉,让我们在遇到错误时有一种解决办法,
但是弊就是它其实降低了代码的可读性,所以只有在一些未知情况下才应该加上try......except。
其基本语法结构是:
try: 语句块 except 异常 as e: 进行处理 else: 处理相关没有捕捉到异常的情况 finally: 无论怎么样都要执行的情况