作为 Python 初学者,在刚学习 Python 编程时,经常会看到一些报错信息,这一章的笔记主要记录自己学习如何处理异常的过程。
Python 有两种错误很容易辨认:语法错误和异常。
一、语法错误
Python 的语法错误或者称之为解析错,是初学者经常碰到的。
my_list = ['zhai', 'zhang', 'yi', 'xing', 1007]
for index in my_list
print(index)
上述代码由于for循环时忘记加冒号导致语法错误。系统会报错,语法分析器会指出出错的一行,并且在最先找到的错误的位置标记了一个小小的箭头。
二、异常
即便 Python 程序的语法是正确的,在运行它的时候,也有可能发生错误。运行期检测到的错误被称为异常。
异常即是一个事件,该事件会在程序执行过程中发生,影响了程序的正常执行。
一般情况下,在Python无法正常处理程序时就会发生一个异常。
异常是Python对象,表示一个错误。
当Python脚本发生异常时我们需要捕获处理它,否则程序会终止执行。
python的标准异常如下所示。
更完整的异常请点击:@https://www.runoob.com/python/python-exceptions.html
较常见的异常如下所示:
1.AssertionError
my_list = [ ]
assert len(my_list) > 0
Python assert(断言)用于判断一个表达式,在表达式条件为 false 的时候触发异常。
2.AttributeError
my_list = [ ]
print(my_list.zhai)
该列表中没有zhai这个属性所以报错。
3.IndexError
my_list = [1, 2, 3]
print(my_list[3])
定义的列表下标范围为0、1、2,非法访问3。
4.KeyError
dic = {'one': 1, 'two': 2, 'three': 3}
print(dic['four']) # 会报错
print(dic.get('four')) # 不会报错
字典中没有key值为four的元素。
5.NameError
print(zhai)
访问了一个不存在的变量zhai。
6.TypeError
print(1 + 'zhai')
数字类型和字符串类型是不能直接相加的。
三、异常处理
捕捉异常可以使用try/except语句。
try/except语句用来检测try语句块中的错误,从而让except语句捕获异常信息并处理。
如果你不想在异常发生时结束你的程序,只需在try里捕获它。
以下为简单的try…except…else的语法:
try:
<语句> #运行别的代码
except <名字>:
<语句> #如果在try部份引发了'name'异常
except <名字>,<数据>:
<语句> #如果引发了'name'异常,获得附加的数据
else:
<语句> #如果没有异常发生
try的工作原理是,当开始一个try语句后,python就在当前程序的上下文中作标记,这样当异常出现时就可以回到这里,try子句先执行,接下来会发生什么依赖于执行时是否出现异常。
- 如果当try后的语句执行时发生异常,python就跳回到try并执行第一个匹配该异常的except子句,异常处理完毕,控制流就通过整个try语句(除非在处理异常时又引发新的异常)。
- 如果在try后的语句里发生了异常,却没有匹配的except子句,异常将被递交到上层的try,或者到程序的最上层(这样将结束程序,并打印默认的出错信息)。
- 如果在try子句执行时没有发生异常,python将执行else语句后的语句(如果有else的话),然后控制流通过整个try语句。
【一个处理异常实例】
f = open('zhai.txt') # 系统中不存在该文件
print(f.read())
f.close()
以上代码会出现FileNotFoundError的错误。所以我们需要用try…except来处理异常:
try:
f = open('zhai.txt')
print(f.read())
f.close()
except OSError: # python3将FileNotFoundError归为OSError
print('文件出错啦/(ㄒoㄒ)/~~')
# 运行结果为文件出错啦/(ㄒoㄒ)/~~
对于程序员而言,我们更想要知道更多错误信息,那么我们就可以使用一个变量将错误原因传给该变量,如下所示:
try:
f = open('zhai.txt')
print(f.read())
f.close()
except OSError as reason:
print('文件出错啦/(ㄒoㄒ)/~~\n错误的原因是:' + str(reason))
# 运行结果为文件出错啦/(ㄒoㄒ)/~~
# 错误的原因是:[Errno 2] No such file or directory: 'zhai.txt'
1.使用except而不带任何异常类型
可以不带任何异常类型使用except,语法格式如下:
try:
正常的操作
......................
except:
发生异常,执行这块代码
......................
else:
如果没有异常执行这块代码
以上方式try-except语句捕获所有发生的异常。但这不是一个很好的方式,我们不能通过该程序识别出具体的异常信息。因为它捕获所有的异常。(不推荐使用)
2.使用except而带多种异常类型
语法格式如下:
try:
正常的操作
......................
except(Exception1[, Exception2[,...ExceptionN]]]):
发生以上多个异常中的一个,执行这块代码
......................
else:
如果没有异常执行这块代码
具体实现如下实例:
try:
f = open('zhai.txt')
print(f.read())
f.close()
except (OSError, TypeError) as reason: #发生这两个异常中的任意一个,执行这块代码
print('出错啦/(ㄒoㄒ)/~~\n错误的原因是:' + str(reason))
也可以显示不同的异常类型的错误原因,如下所示:
try:
sum(1 + '1') # 该语句会出现TypeError
f = open('zhai.txt') # 该语句会出现OSError
print(f.read())
f.close()
except OSError as reason:
print('文件出错啦/(ㄒoㄒ)/~~\n错误的原因是:' + str(reason))
except TypeError as reason:
print('类型出错啦/(ㄒoㄒ)/~~\n错误的原因是:' + str(reason))
# 运行结果为类型出错啦/(ㄒoㄒ)/~~
# 错误的原因是:unsupported operand type(s) for +: 'int' and 'str'
3.try-finally 语句
try-finally 语句无论是否发生异常都将执行最后的代码。
语法格式为:
try:
<语句>
finally:
<语句> #退出try时总会执行
raise
一个简单的实例:
try:
f = open('zhai.txt', 'w')
print(f.write("我存在了!"))
sum = 1 + 'i'
f.close() # 执行到该步骤时刚写入文件中的数据才会保存
except (OSError, TypeError) as reason:
print('出错啦/(ㄒoㄒ)/~~\n错误的原因是:' + str(reason))
print(open('zhai.txt', 'r').read()) # "我存在了!"并没有被成功写入该文件
运行结果为:
上述代码中可以看出写入文件中的数据并没有成功保存,是因为在 f.close() 前有一个TypeError类型的错误,从而程序会转去处理异常。为了避免这个问题,我们将 f.close() 这条必须要执行的语句放入finally代码块中,如下所示:
try:
f = open('zhai.txt', 'w')
print(f.write("我存在了!"))
sum = 1 + 'i'
except (OSError, TypeError) as reason:
print('出错啦/(ㄒoㄒ)/~~\n错误的原因是:' + str(reason))
finally:
f.close() # 成功保存
print(open('zhai.txt', 'r').read()) # "我存在了!"成功写入该文件
修正后的执行结果为:
四、raise触发异常
我们可以使用raise语句自己触发异常,raise语法格式如下:
raise [Exception [, args [, traceback]]]
一个异常可以是一个字符串,类或对象。 Python的内核提供的异常,大多数都是实例化的类,这是一个类的实例的参数。
print(1 / 0) # 该语句会引发一个ZeroDivisionError的异常
我们也可以通过raise来引发ZeroDivisionError异常:
raise ZeroDivisionError
运行结果为:
也可以在引发异常时,输出信息:
raise ZeroDivisionError("此处为除数为零的异常!")
此时的运行结果为:
定义一个异常非常简单,如下所示:
def functionName( level ):
if level < 1:
raise Exception("Invalid level!", level)
# 触发异常后,后面的代码就不会再执行
functionName(0) # 此时满足level < 1
运行结果为:
为了能够捕获这个raise触发的异常,"except"语句必须有用相同的异常来抛出类对象或者字符串。
def functionName( level ):
if level < 1:
raise Exception("Invalid level!", level)
# 触发异常后,后面的代码就不会再执行
try:
functionName(0) # 触发异常
except Exception as err: # 使用相同的异常Exception
print("这里是对raise触发异常的处理!")
else:
print("其他异常!")
# 运行结果为:这里是对raise触发异常的处理!
五、用户自定义异常
通过创建一个新的异常类,程序可以命名它们自己的异常。异常应该是典型的继承自Exception类,通过直接或间接的方式。
以下为与RuntimeError相关的实例,实例中创建了一个类,基类为RuntimeError,用于在异常触发时输出更多的信息。
class Networkerror(RuntimeError):
def __init__(self, arg): # 构造器
self.args = arg # 输出为元组类型
在你定义以上类后,你可以触发该异常。在try语句块中,用户自定义的异常后执行except块语句,变量 e 是用于创建Networkerror类的实例。如下所示:
try:
raise Networkerror("Bad hostname")
except Networkerror as e:
print(e.args)
以上代码的运行结果为: