我又来学习了。
对象
表示异常状态,并在遇到错误时引发异常;终止
并显示错误信息;实例
;继续进行
,而不是放任整个程序失败后终止。让错误尽在你的掌握之中。
raise
语句用来引发异常。raise语句的参数是一个类(必须是Exception的子类)或者是实例。当用类作为参数时,将自动创建一个实例(归根结底就是一个异常实例)。
raise Exception
结果:
可以在参数后加上自己设定的相关信息。
raise Exception("我是一个天大的错误!")
结果:
Python内置了多个异常类,了解一下:
我们可以创建自己的异常类。比如模拟宇宙飞船飞行的python程序正在运行,突然飞船内的“超光速推进装置”过载,这时候就会引发异常。为了更好地表示错误,我们不必使用python内置的类,转而创建自己的子类HyperdriverError,用它来表示“超光速推进装置”的错误,这样就会更自然一些。
务必要直接或者间接的继承Exception(从它的子类继承就是间接)。
类似于这样:
class HyperdriverError(Exception):
pass
如果出现了异常,可以逮住它们,把它们都“做掉”。
类似于java中的try/catch,python中使用try/except处理异常。
一个?:比如要实现两个数相除的程序,但是如果分母为0,就会引发错误使得程序终止,我们可以捕捉ZeroDivisionError来处理异常。
try:
x = int(input("请输入分子:"))
y = int(input("请输入分母:"))
print(x/y)
except ZeroDivisionError:
print("分母不能为0!")
结果:
捕获了异常之后,如果想要使得异常继续向上一层传播,可以再次使用没有参数的raise重新引发异常。
书中的一个?:
class Calculator:
muffled = False
def calc(self, expr):
try:
return eval(expr)
except ZeroDivisionError:
if self.muffled:
print("分母不能为0!")
else:
raise
类中定义了一个抑制开关muffled,当开关打开时,如果发生了除零错误,异常就会被捕捉到,打印“分母不能为0!”;开关关闭时,就会重新引发异常(raise),使异常向上传播。需要注意的时,在这个例子中当异常被捕捉到时,由于函数calc没有返回值,将返回None。
例子的运行演示:
# 正常情况
In [2]: calculator = Calculator()
...: calculator.calc("10/2")
Out[2]: 5.0
# 关闭抑制时
In [3]: calculator.calc("10/0")
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
in
----> 1 calculator.calc("10/0")
ZeroDivisionError: division by zero
# 开启抑制时
In [4]: calculator.muffled = True
In [5]: calculator.calc("10/0")
分母不能为0!
可以看到,关闭抑制时,异常继续向上传播,如果在上一层没有对其进行捕捉,就会引发程序错误并终止。
在except子句中,可以通过raise引发别的异常。在这种情况下,引发except的原始异常会作为异常上下文
储存起来,并会显示在最终的错误消息中。
栗子:
try:
1/0
except ZeroDivisionError:
raise ValueError("捕捉除零异常的时候再次引发了我...")
结果:
raise...from...
提供自己的上下文,也可以用None禁用上下文。
try:
1/0
except ZeroDivisionError:
raise ValueError("捕捉除零异常的时候再次引发了我...") from ValueError
结果:
try:
1/0
except ZeroDivisionError:
raise ValueError("捕捉除零异常的时候再次引发了我...") from None
结果:
在8.3节下的示例中,只捕获了除零异常,如果用户输入了非数字,会引发TypeError,使程序发生错误终止,因此可以这样写:
try:
x = int(input("请输入分子:"))
y = int(input("请输入分母:"))
print(x/y)
except ZeroDivisionError:
print("分母不能为0!")
except TpyeError:
print("必须输入数字!")
这样就可以处理多个异常情况。相比频繁使用if,利用异常机制可以使代码更整洁。
可以用括号把想要捕捉的异常写进一个元组,这样就不用频繁地去写except子句了。
try:
x = int(input("请输入分子:"))
y = int(input("请输入分母:"))
print(x/y)
except (ZeroDivisionError, TypeError, NameError):
print("输入错误!")
当然,缺点是每种错误都会打印同一种信息。
为了弥补上面的缺陷,可以显式的捕捉异常,然后打印此类异常的相关信息:
try:
x = int(input("请输入分子:"))
y = int(input("请输入分母:"))
print(x/y)
except (ZeroDivisionError, TypeError, NameError) as e:
print(e)
不指定异常类,就会捕捉所有的错误:
try:
x = int(input("请输入分子:"))
y = int(input("请输入分母:"))
print(x/y)
except:
print("出现了一些错误")
这样做并不能知道发生了什么错误,最好还是显式捕捉错误Exception as e然后打印之。
可以添加else
语句,如果程序正常运行,那么else
中的语句就会被执行:
try:
print("正在执行...")
except Exception as e:
print(e)
else:
print("执行顺利!")
结果:
利用else语句可以对上面的程序进行改良,如果用户输入有误,会让用户重复输入,直到对了为止:
while True:
try:
x = int(input("请输入分子:"))
y = int(input("请输入分母:"))
print(x / y)
except Exception as e:
print(e)
else:
break
运行:
敲代码的时候犯了一个错误,写成了While(True),不应该加括号。
还有finally
子句,可用于在发生异常时执行清理工作,这个子句通常是与try配套的,无论try中发生什么异常,最终都会执行
finally子句。
书中的栗子:
try:
x = 1/0
finally:
print("正在执行finally")
del x
结果:
try
、
except
、
else
、
finally
结合使用。
函数中的异常会一层一层向上传递(到调用它的地方),如果都没有被处理,程序就会终止并打印TraceBack(栈追踪)信息。
栗子:
def faulty():
raise Exception("Something is wrong...")
def no_handle():
faulty()
def handle_exception():
try:
faulty()
except:
print("Exception handled")
运行no_handle():
no_handle()
结果:
运行handle_exception():
handle_exception()
结果:
并不会打印栈追踪信息,而是执行except子句。
自己瞎折腾:
def faulty():
raise Exception("Something is wrong...")
def no_handle():
try:
faulty()
print("555")
except:
print("666")
def handle_exception():
try:
no_handle()
print("777")
except:
print("888")
handle_exception()
结果:
两段代码:
def describe_person(person):
print('Description of', person['name'])
print('Age:', person['age'])
if 'occupation' in person:
print('Occupation:', person['occupation'])
输出:
def describe_person(person):
print('Description of', person['name'])
print('Age:', person['age'])
try:
print('Occupation:', person['occupation'])
except KeyError: pass
输出: