一. 什么是异常
1. 异常即是一个事件,该事件会在程序执行过程中发生,影响了程序的正常执行。
2. 一般情况下,Python无法正常处理程序时就会发生一个异常。
3. 异常是 Python 对象,表示一个错误。当 Python 脚本发生异常时我们需要捕获处理它,否则程序会终止执行。
4. 每一个异常都是一些类的实例,这些实例可以被引用,并且可以用很多种方法进行捕捉,使得错误可以被处理,而不是让整个程序失败
二. 异常处理
处理程序中的异常,最简单的是使用 try 语句处理。
try 语句的基本形式是 try/execpt。
try / except 语句用来检测 try 语句块中的错误,从而让 except 语句捕获异常信息并处理。如果你不想在异常发生时结束你的程序,只需在try里捕获它。
语法如下:
try:
<语句> #运行的代码(需要检测的语句)
except <名字>: #发生异常时的操作。名字为对应错误的名称
<语句>
1、try的工作原理是,当开始一个try语句后,python就在当前程序的上下文中作标记,这样当异常出现时就可以回到做标记的地方。
2、try子句先执行,接下来会发生什么依赖于执行时是否出现异常。如果当try后的语句执行时发生异常,python就跳回到try并执行第一个匹配该异常的except子句,每一个try,都必须至少有一个except。异常处理完毕,控制流就通过整个try语句(除非在处理异常时又引发新的异常)。
3、如果在try后的语句里发生了异常,却没有匹配的except子句,异常将被递交到上层的try,或者到程序的最上层(这样将结束程序,并打印缺省的出错信息)。
4、如果在try子句执行时没有发生异常,python将执行else语句后的语句(如果有else的话),然后控制流通过整个try语句。
5、当我们认为某些代码可能会出错时,就可以用try来运行这段代码,如果执行出错,则后续代码不会继续执行,而是直接跳转至错误处理代码,即except语句块,执行完except后,如果有finally语句块,则执行finally语句块
6、在try程序段中,我们放入容易犯错的部分。我们可以跟上except,来说明如果在try部分的语句发生错误时,程序该做的事情。如果没有发生异常,则except部分被跳过。
def exp (x,y):
try:
a = x/y
print('a='.a)
return a
except Exception:
print('程序出现异常,异常信息:被除数为 0')
exp(2, 0)
执行结果:
程序出现异常,异常信息:被除数为 0
三. 抛出异常
1、如果只想知道是否抛出了异常,并不想处理,使用一个简单的raise语句就可以再次把异常抛出。一旦执行了 raise 语句,raise后面的语句将不能执行。
2、用 raise 语句来引发一个异常。异常/错误对象必须有一个名字,且它们应是 Error 或 Exception 类的子类。
3、raise关键字后面需要指定你抛出的异常类型为了能够捕获异常,"except"语句必须有用相同的异常来抛出类对象或者字符串。
raise语法格式如下:
raise [ Exception [, args [, traceback] ] ]
- 语句中 Exception 是异常的类型(例如,NameError)参数标准异常中任一种,args 是自已提供的异常参数。最后一个参数是可选的(在实践中很少使用),如果存在,是跟踪异常对象。
- 格式:raise 异常名称(‘异常描述’)
>>> raise Exception
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
Exception
>>>
>>> raise NameError('This is NameError')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: This is NameError
如果只想知道是否抛出了,并不想处理,使用一个简单的 raise 语句就可以再次把异常抛出。
这个是把最近一次产生的异常重新抛出来,交给上层 (我已经知道了这个异常并且捕获到了,但是我不处理,而由我的上层调用处理)
1、异常会在捕获之后再次触发同一个异常。
2、如果要捕获异常后要重复抛出,请使用raise,后面不要带任何参数或信息
3、except语句不是必须的,finally语句也不是必须的,但是二者必须要有一个,否则就没有try的意义了。
>>> try :
... raise NameError('This is NameError')
... except NameError:
... print(' An exception happened!') # 后面不加 raise
...
An exception happened!
>>>
>>>
>>> try :
... raise NameError('This is NameError')
... except NameError:
... print(' An exception happened!')
... raise # 后面加一个 raise
...
An exception happened!
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
NameError: This is NameError
raise关键字后面是抛出是一个通用的异常类型(Exception),一般来说抛出的异常越详细越好,Python在exceptions模块内建了很多的异常类型,通过使用dir函数来查看exceptions中的异常类型。
异常名称 | 描述 |
Exception | 常规错误的基类 |
AttributeError | 对象没有这个属性 |
IOError | 输入/输出操作失败 |
IndexError | 序列中没有此索引 |
KeyError | 映射中没有这个键 |
NameError | 未申明/初始化对象(没有属性) |
SyntaxError | Python 语法错误 |
SystemError | 一般解释器系统错误 |
ValueError | 传入无效的参数 |
四. 捕获多个异常
try:
<语句> #运行别的代码
except <名字>:
<语句> #如果在try部分引发了'name1'异常
except <名字>,<数据>:
<语句> #如果引发了'name2'异常,获得附加的数据
首先,执行 try 子句 ( 在关键字 try 和关键字 except 之间的语句)。如果没有发生异常,忽略 except 子句,try 子句执行后结束。如果在执行 try 子句的过程中发生异常,try 子句余下的部分就会被忽略。如果异常的类型和 except 之后的名称相符,对应的 except 子句就会被执行。最后执行 try 语句之后的代码。如果一个异常没有与任何 except 匹配,这个异常就会传递到上层的 try 中。一个 try 语句可能包含多个 except 子句,分别处理不同的异常,但最多只有一个分支会被执行。
处理程序将只针对对应 try子句中的异常进行处现,而不会处理其他异常语句中的异常
def exp (x,y):
try:
a = x/y
b = name
except ZeroDivisionError:
print("this is ZeroDivisionErro")
except NameError:
print("this is NameError")
exp(2, 0)
执行结果:
this is ZeroDivisionErro
五. 使用一个块捕捉多个异常
如果需要使用一个块捕捉多个类型异常,可以将异常类型作为元组列出。使用该方式时,遇到的异常类型是元组中的任意一个,都会走异常流程。
def exp (x,y):
try:
a = x/y
b = name
except (ZeroDivisionError,NameError,TypeError):
print("one of ZeroDivisionError or NameError or TypeError")
exp(2, 0)
执行结果:
one of ZeroDivisionError or NameError or TypeError
六. 捕捉对象
如果希望在except字句中访问异常对象本身,也就是看到一个异常对象真正的异常信息,而不是输出自己定义的异常信息,可以使用as e的形式,称之为捕捉异常。
def exp (x,y):
try:
a = x/y
b = name
except (ZeroDivisionError,NameError,TypeError) as e :
print(e)
exp(2, 0)
执行结果:
division by zero
def exp (x,y):
try:
b = name
a = x/y
except (ZeroDivisionError,NameError,TypeError) as e :
print(e)
exp(2, 0)
执行结果:
name 'name' is not defined
七 . 全捕捉
#!/usr/bin/python3
# -*- coding:UTF-8 -*-
def exp (x,y):
try:
a = x/y
b = name
except (ZeroDivisionError,NameError,TypeError) as e :
print(e)
exp(2, '')
执行结果:
unsupported operand type(s) for /: 'int' and 'str'
由上面可以看出即使程序能自动处理很多种异常,但是还是会有部分异常会被遗漏(逃过了try\except的检查)。在这种情况下,与其使用异常捕捉的 try\except 语句隐藏异常,还不如让程序立即崩溃
#!/usr/bin/python3
# -*- coding:UTF-8 -*-
def exp (x,y):
try:
a = x/y
b = name
except :
print(" Error happenend")
exp(2, '')
执行结果:
Error happenend
1、由上面例子可以看出,可以在except子句中忽略所有异常类,从而让程序输出自己定义的异常信息
2、从实用性方面来讲,不建议这么做,因为这样会隐藏所有的的异常,从而不好找到问题。建议使用抛出异常的方式处理
八. 异常中的 else
try:
<语句> #运行别的代码
except <名字>:
<语句> #如果在try部份引发了'name1'异常
except <名字>,<数据>:
<语句> #如果引发了'name2'异常,获得附加的数据
else:
<语句> #如果没有异常发生
如果在 try 子句执行时没有发生异常,就会执行 else 语句后的语句。
使用 else 子句可以避免一些意想不到而 except 又没有捕获的异常。
#!/usr/bin/python3
# -*- coding:UTF-8 -*-
def exp (x,y):
try:
a = x/y
except :
print(" Error happenend")
else:
print("it went as except")
print(a)
exp(2, 1)
执行结果:
it went as except
2.0
九. finally 子句
python 中的 finally 子句需要和 try 子句一起使用,组成 try / finally 的语句形式,try / finally 语句表示无论发生异常与否都将执行最后的代码
#!/usr/bin/python3
# -*- coding:UTF-8 -*-
def exp (x,y):
try:
a = x/y
finally:
print("no matter what happened,i will show in front of you")
exp(2, 0)
执行结果:
pydev debugger: starting (pid: 6256)
no matter what happened,i will show in front of you
Traceback (most recent call last):
File "C:\eclipse_JavaFX\plugins\org.python.pydev.core_6.4.3.201807050139\pysrc\pydevd.py", line 1715, in <module>
main()
File "C:\eclipse_JavaFX\plugins\org.python.pydev.core_6.4.3.201807050139\pysrc\pydevd.py", line 1709, in main
globals = debugger.run(setup['file'], None, None, is_module)
File "C:\eclipse_JavaFX\plugins\org.python.pydev.core_6.4.3.201807050139\pysrc\pydevd.py", line 1062, in run
return self._exec(is_module, entry_point_fn, module_name, file, globals, locals)
File "C:\eclipse_JavaFX\plugins\org.python.pydev.core_6.4.3.201807050139\pysrc\pydevd.py", line 1069, in _exec
pydev_imports.execfile(file, globals, locals) # execute the script
File "C:\eclipse_JavaFX\plugins\org.python.pydev.core_6.4.3.201807050139\pysrc\_pydev_imps\_pydev_execfile.py", line 25, in execfile
exec(compile(contents+"\n", file, 'exec'), glob, loc)
File "D:\Users\li_yx\workspace\pythontest\pytest.py", line 10, in <module>
exp(2, 0)
File "D:\Users\li_yx\workspace\pythontest\pytest.py", line 6, in exp
a = x/y
ZeroDivisionError: division by zero
try / finally语句也可以和except、else一起组合使用。但要记得 else在except 之后,finally 在 except和else 之后(这种组合中except的作用为:用于截获可能发生的异常,使得程序不报错而执行except所属语句)
#!/usr/bin/python3
# -*- coding:UTF-8 -*-
def exp (x,y):
try:
a = x/y
except Exception as e:
print(e)
finally:
print("no matter what happened,i will show in front of you")
exp(2, 0)
执行结果:
division by zero
no matter what happened,i will show in front of you
十. 自定义异常
1、python允许程序员自定义异常,用于描述python中没有涉及的异常情况,自定义异常必须继承 Exception类,自定义异常按照命名规范以"Error"结尾,显示地告诉程序员这是异常。自定义异常使用raise语句引发,而且只能通过人工方式触发。
2、因为错误就是类捕获一个错误就是捕获该类的一个实例,因此错误并不是凭空产生的,而是由一些不合理的部分导致的。python的内置函数会抛出很多类型的错误,我们自己编写的函数也可以抛出错误。如果要抛出错误,那么可以根据需要定义一个错误的类,选择好继承关系,然后用raise语句抛出一个错误的实例
#!/usr/bin/python3
# -*- coding:UTF-8 -*-
class MyError(Exception):
def __init__(self):
pass
def __str__(self):
return ('this is self define error')
def my_error_test():
try:
raise MyError()
except MyError as e:
print("exception inof:",e)
my_error_test()
执行结果:
exception inof: this is self define error
十一. 异常和函数
如果异常在函数内引发而不被处理,就会传播至函数调用的地方。如果异常在调用的地方还是没有被处理,则会继续传播,一直到达主程序。如果在主程序也没有做异常处理,异常就会被python解释器捕获,输出一个错误信息,然后退出程序
def division_fun(x,y):
return x/int(y)
def exp_fun(x,y):
return division_fun(x, y) * 10
def main(x,y):
exp_fun(x, y)
main(2, 0)
执行结果:
pydev debugger: starting (pid: 10936)
Traceback (most recent call last):
File "C:\eclipse_JavaFX\plugins\org.python.pydev.core_6.4.3.201807050139\pysrc\pydevd.py", line 1715, in <module>
main()
File "C:\eclipse_JavaFX\plugins\org.python.pydev.core_6.4.3.201807050139\pysrc\pydevd.py", line 1709, in main
globals = debugger.run(setup['file'], None, None, is_module)
File "C:\eclipse_JavaFX\plugins\org.python.pydev.core_6.4.3.201807050139\pysrc\pydevd.py", line 1062, in run
return self._exec(is_module, entry_point_fn, module_name, file, globals, locals)
File "C:\eclipse_JavaFX\plugins\org.python.pydev.core_6.4.3.201807050139\pysrc\pydevd.py", line 1069, in _exec
pydev_imports.execfile(file, globals, locals) # execute the script
File "C:\eclipse_JavaFX\plugins\org.python.pydev.core_6.4.3.201807050139\pysrc\_pydev_imps\_pydev_execfile.py", line 25, in execfile
exec(compile(contents+"\n", file, 'exec'), glob, loc)
File "D:\Users\li_yx\workspace\pythontest\pytest.py", line 11, in <module>
main(2, 0)
File "D:\Users\li_yx\workspace\pythontest\pytest.py", line 8, in main
exp_fun(x, y)
File "D:\Users\li_yx\workspace\pythontest\pytest.py", line 5, in exp_fun
return division_fun(x, y) * 10
File "D:\Users\li_yx\workspace\pythontest\pytest.py", line 2, in division_fun
return x/int(y)
ZeroDivisionError: division by zero
【注】异常信息是以堆栈的形式被抛出的,因而是从下往上查看。
十二. assert 语句触发异常
assert 语句根据后面的表达式的真假来控制程序流。若为True,则往下执行。若为 False,则中断程序并调用默认的异常处理器,同时输出指定的提示信息。
格式:
assert expression , 'information'
>>> def testAssert(x):
... assert x<1 , 'Invalid value'
... print(' No Error')
...
>>>
>>> testAssert(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in testAssert
AssertionError: Invalid value
>>>
>>>
>>> testAssert(0)
No Error