Python 异常的捕获和抛出细节
age = int(input("Age:")) #输入年龄,整数类型 # try....except...finally ===== 捕获异常 # raise ====== 抛出异常 class AgeError(Exception): #自定义的一个异常类,继承自Exception父类 pass if age < 0 or age > 120: raise AgeError #如果满足条件,抛出自定义类 else: print(age) #不满足if条件,正常运行
except
语句不是必须的,finally
语句也不是必须的,但是二者必须要有一个,否则就没有try
的意义了。except
语句可以有多个,Python会按except
语句的顺序依次匹配你指定的异常,如果异常已经处理就不会再进入后面的except
语句。except
语句可以以元组形式同时指定多个异常,参见下面的代码:a=3 b='a' try: print(a / b) except (ZeroDivisionError, TypeError) as e: print(e) #运行结果: #unsupported operand type(s) for /: 'int' and 'str' a=3 b=0 try: print(a / b) except (ZeroDivisionError, TypeError) as e: print(e) #运行结果: #division by zero
except
语句后面如果不指定异常类型,则默认捕获所有异常,你可以通过logging或者sys模块获取当前异常。如果要捕获异常后要重复抛出,请使用
raise
,后面不要带任何参数或信息。不建议捕获并抛出同一个异常,请考虑重构你的代码。
不建议在不清楚逻辑的情况下捕获所有异常,有可能你隐藏了很严重的问题。
尽量使用内置的异常处理语句来替换
try/except
语句,比如with
语句,getattr()
方法。一般你在自定义异常类型时,需要考虑的问题应该是这个异常所应用的场景。如果内置异常已经包括了你需要的异常,建议考虑使用内置的异常类型。比如你希望在函数参数错误时抛出一个异常,你可能并不需要定义一个
InvalidArgumentError
,使用内置的ValueError
即可。在Python中,为了保持异常的完整信息,那么你捕获后再次抛出时千万不能在
raise
后面加上异常对象,否则你的trace
信息就会从此处截断。以下代码是最简单的重新抛出异常的做法,也是推荐的做法。
def f1(): print(1/0) def f2(): try: f1() except Exception as e: raise # don't raise e f2() #抛出异常的作用是使程序按照我们需要的状态告诉我们错误在哪,从而追踪错误并排除它。
从
Exception
的层级结构来看,BaseException
是最基础的异常类,Exception
继承了它。BaseException
除了包含所有的Exception
外还包含了SystemExit
,KeyboardInterrupt
和GeneratorExit
三个异常。由此看来你的程序在捕获所有异常时更应该使用
Exception
而不是BaseException
,因为被排除的三个异常属于更高级别的异常,合理的做法应该是交给Python的解释器处理。精确找到臭虫(bug)是一件好事情,因此,尽量别用baseexception,虽然,看起来你会省事,但对问题的定位帮助并不是很大。
def div(a, b): try: print(a / b) except ZeroDivisionError: print("Error: b should not be 0 !!") except Exception as e: print("Unexpected Error: {}".format(e)) else: print('Run into else only when everything goes well') finally: print('Always run into finally block.') # tests div(2, 0) #会抛出哪个异常?猜猜看结果! div(2, 'bad type') #这个会抛出哪个异常?猜猜看结果! div(1, 2) #这个不会抛出异常?猜猜看结果!