python入门——天池Task1(day3异常处理——try/except/else/finally,raise)
5 异常处理
5.1 try - except 语句
表达式:
try:
检测范围
except Exception[as reason]:
出现异常后的处理代码
try 语句按照如下方式工作:
1.首先,执行try子句(在关键字try和关键字except之间的语句)
2.如果没有异常发生,忽略except子句,try子句执行后结束。
3.如果在执行try子句的过程中发生了异常,那么try子句余下的部分将被忽略。如果异常的类型和except之后的名 称相符,那么对应的except子句将被执行。最后执行try - except语句之后的代码。
4.如果一个异常没有与任何的except匹配,那么这个异常将会传递给上层的try中。
异常处理的意义:当程序运行出现错误时,整个程序就会中断,不再向下执行了,异常处理就可以解决这个问题。
【例子1】程序在执行第3行的过程中出现异常,后面的程序也执行不了了
a = 2
print(a)
a = z
print(a)
print("程序在前面异常中断,此句执行不了")
#输出结果为:
'''
2
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-26-c9adf3acc9f8> in <module>
1 a = 2
2 print(a)
----> 3 a = z
4 print(a)
5 print("程序在前面异常中断,此句执行不了")
NameError: name 'z' is not defined
'''
【例子2】使用try - except语句,除了try下面代码块中,发生异常及其之后的语句不执行(即:a = z
print(a)),后面其他的语句正常执行
try:
a = 2
print(a)
a = z
print(a)
except:
print('Error')
print("程序在前面异常中断,此句执行不了")
#输出结果为:
'''
2
Error
程序在前面异常中断,此句执行不了
'''
【例子3】try下面的代码块没有异常发生
try:
a = 2
print(a)
except:
print('Error')
print("程序在前面异常中断,此句执行不了")
#输出结果为:
'''
2
程序在前面异常中断,此句执行不了
'''
从【例子1】,【例子2】以上大致可以知道:
(1)程序先执行try和except之间的代码;
(2)在执行try和except之间的代码时出现错误,就不再继续向下执行了(在a = z时程序出错,后面的print(a)就不再执行了),而是跳转到执行except下的代码块;
(3)最后再继续向下执行。
从【例子3】可以看出:如果try下面的代码块没有异常发生,则except下的代码将会被忽略。
同时也可以存在多个except,分别来处理不同的特定的异常。最多只有一个分支会被执行。
【例子4】
此处使用了3 个except,分别在后面跟了SystemError,NameError,AssertionError,当try下面的代码块运行出错时,可以自动与except后面给出的错误类型进行匹配,并执行相应except代码块下的语句。
try:
a = 2
print(a)
a = z
print(a)
except SystemError:
print('SystemError')
except NameError:
print('NameError')
except AssertionError:
print('AssertionError')
print("程序在前面异常中断,此句执行不了")
#输出结果为:
'''
2
NameError
程序在前面异常中断,此句执行不了
'''
【例子5】
当出现的报错与except中的错误类型都不匹配时,又回到了最开始【例子1】一样的情况,此时的try - except是没有起到作用的。
try:
a = 2
print(a)
a = z
print(a)
except SystemError:
print('SystemError')
except EnvironmentError:
print('EnvironmentError')
except AssertionError:
print('AssertionError')
print("程序在前面异常中断,此句执行不了")
#输出结果为:
'''
2
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-45-89ba6b3a8bb0> in <module>
2 a = 2
3 print(a)
----> 4 a = z
5 print(a)
6 except SystemError:
NameError: name 'z' is not defined
'''
【例子6】
使用except而不带任何异常类型,这不是一个很好的方式,我们不能通过该程序识别出具体的异常信息,因为它捕获所有的异常。
为了避免没有匹配到异常,同时有使程序中断,可将代码进行如下修改:
try:
a = 2
print(a)
a = z
print(a)
except SystemError:
print('SystemError')
except EnvironmentError:
print('EnvironmentError')
except AssertionError:
print('AssertionError')
except:
print('报错类型与except中设定的错误类型不匹配')
print("程序在前面异常中断,此句执行不了")
#输出为:
'''
2
报错类型与except中设定的错误类型不匹配
程序在前面异常中断,此句执行不了
'''
【例子7】
dict1 = {'a': 1, 'b': 2, 'v': 22}
try:
x = dict1['y']
except LookupError:
print('查询错误')
except KeyError:
print('键错误')
else:
print(x)
#输出为:
'''
查询错误
'''
【例子8】
dict1 = {'a': 1, 'b': 2, 'v': 22}
try:
x = dict1['y']
except KeyError:
print('键错误')
except LookupError:
print('查询错误')
else:
print(x)
#输出为:
'''
键错误
'''
从【例子7】,【例子8】中可以看出:try-except-else语句尝试查询不在dict中的键值对,从而引发了异常。这一异常准确地说应属于KeyError,但由于KeyError是LookupError的子类,且将LookupError置于KeyError之前,因此程序优先执行该except代码块。所以,使用多个except代码块时,必须坚持对其规范排序,要从最具针对性的异常到最通用的异常。
【例子9】
一个 except 子句可以同时处理多个异常,这些异常将被放在一个括号里成为一个元组。
try:
s = 1 + '1'
int("abc")
f = open('test.txt')
print(f.read())
f.close()
except (OSError, TypeError, ValueError) as error:
print('出错了!\n原因是:' + str(error))
#输出为:
'''
出错了!
原因是:unsupported operand type(s) for +: 'int' and 'str'
'''
5.2 try - except - else 语句
如果在try子句执行时没有发生异常,Python将执行else语句后的语句。
表达式:
try:
检测范围
except:
出现异常后的处理代码
else:
如果没有异常执行这块代码
【例子10】try子句执行时发生异常,else下的代码块不执行。
try:
a = 2
print(a)
a = x
print(a)
except SystemError:
print('SystemError')
except EnvironmentError:
print('EnvironmentError')
except AssertionError:
print('AssertionError')
except:
print('报错类型与except中设定的错误类型不匹配')
else:
print('程序没有错误')
print("程序在前面异常中断,此句执行不了")
#输出为:
'''
2
报错类型与except中设定的错误类型不匹配
程序在前面异常中断,此句执行不了
'''
【例子11】try子句执行时没有发生异常,将执行else下的代码块。
try:
a = 2
print(a)
z = a
print(z)
except SystemError:
print('SystemError')
except EnvironmentError:
print('EnvironmentError')
except AssertionError:
print('AssertionError')
except:
print('报错类型与except中设定的错误类型不匹配')
else:
print('程序没有错误')
print("程序在前面异常中断,此句执行不了")
#输出为:
'''
2
2
程序没有错误
程序在前面异常中断,此句执行不了
'''
注意:else语句的存在必须以except语句的存在为前提,在没有except语句的try语句中使用else语句,会引发语法错误。
else作用:没有else语句,当执行完try语句后,无法知道是没有发生异常,还是发生了异常并被处理过了。通过else可以清楚的区分开。
5.3 try - except - finally 语句
不管try子句里面有没有发生异常,finally子句都会执行。
表达式:
try:
检测范围
except Exception[as reason]:
出现异常后的处理代码
finally:
无论如何都会被执行的代码
【例子12】
如果一个异常在try子句里被抛出,而又没有任何的except把它截住,那么这个异常会在finally子句执行后被抛出。
def divide(x, y):
try:
result = x / y
print("result is", result)
except ZeroDivisionError:
print("division by zero!")
finally:
print("executing finally clause")
divide(2, 1)
# result is 2.0
# executing finally clause
divide(2, 0)
# division by zero!
# executing finally clause
divide("2", "1")
# executing finally clause
# TypeError: unsupported operand type(s) for /: 'str' and 'str'
#输出为:
'''
result is 2.0
executing finally clause
division by zero!
executing finally clause
executing finally clause
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-57-16805cf48925> in <module>
15 # division by zero!
16 # executing finally clause
---> 17 divide("2", "1")
18 # executing finally clause
19 # TypeError: unsupported operand type(s) for /: 'str' and 'str'
<ipython-input-57-16805cf48925> in divide(x, y)
1 def divide(x, y):
2 try:
----> 3 result = x / y
4 print("result is", result)
5 except ZeroDivisionError:
TypeError: unsupported operand type(s) for /: 'str' and 'str'
'''
【例子13】try - finally 也可以单用。
同样的,异常要在finally下的代码块执行完之后才抛出。
try:
x = y
print(x)
finally:
print('无论try下面的代码块是否发生错误,finally下的代码块都会执行')
print("程序在前面异常中断,此句执行不了")
#输出为:
'''
无论try下面的代码块是否发生错误,finally下的代码块都会执行
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-58-5d85bca8aabd> in <module>
1 try:
----> 2 x = y
3 print(x)
4 finally:
5 print('无论try下面的代码块是否发生错误,finally下的代码块都会执行')
NameError: name 'y' is not defined
'''
5.6 raise 语句
Python 使用raise语句人为的抛出一个指定的异常。
程序发生异常和程序执行错误,它们完全是两码事,程序由于错误导致的运行异常,是需要程序员想办法解决的;但还有一些异常,是程序正常运行的结果,比如用 raise 手动引发的异常。
raise 语句的基本语法格式为:
raise [exceptionName [(reason)]]
其中,用 [] 括起来的为可选参数,其作用是指定抛出的异常名称,以及异常信息的相关描述。如果可选参数全部省略,则 raise 会把当前错误原样抛出;如果仅省略 (reason),则在抛出异常时,将不附带任何的异常描述信息。
也就是说,raise 语句有如下三种常用的用法:
1.raise:单独一个 raise。该语句引发当前上下文中捕获的异常(比如在 except 块中),或默认引发 RuntimeError 异常。
2.raise 异常类名称:raise 后带一个异常类名称,表示引发执行类型的异常。
3.raise 异常类名称(描述信息):在引发指定类型的异常的同时,附带异常的描述信息。
【例子14】raise引发异常的三种情况
【例子15】
try:
a = input("输入一个数:")
#判断用户输入的是否为数字
if(not a.isdigit()):
raise ValueError("a 必须是数字")
except ValueError as e:
print("引发异常:",repr(e))
#输出为:
'''
输入一个数:a
引发异常: ValueError('a 必须是数字')
'''
可以看到,当用户输入的不是数字时,程序会进入 if 判断语句,并执行 raise 引发 ValueError 异常。但由于其位于 try 块中,因为 raise 抛出的异常会被 try 捕获,并由 except 块进行处理。
因此,虽然程序中使用了 raise 语句引发异常,但程序的执行是正常的,手动抛出的异常并不会导致程序崩溃。
【例子16】
try:
a = input("输入一个数:")
if(not a.isdigit()):
raise ValueError("a 必须是数字")
except ValueError as e:
print("引发异常:",repr(e))
raise
#输出为:
'''
输入一个数:a
引发异常: ValueError('a 必须是数字')
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-28-8ee213d8d387> in <module>
2 a = input("输入一个数:")
3 if(not a.isdigit()):
----> 4 raise ValueError("a 必须是数字")
5 except ValueError as e:
6 print("引发异常:",repr(e))
ValueError: a 必须是数字
'''
这里重点关注位于 except 块中的 raise,由于在其之前我们已经手动引发了 ValueError 异常,因此这里当再使用 raise 语句时,它会再次引发一次。
【例子17】当在没有引发过异常的程序使用无参的 raise 语句时,它默认引发的是 RuntimeError 异常。如下:
try:
a = input("输入一个数:")
if(not a.isdigit()):
raise
except RuntimeError as e:
print("引发异常:",repr(e))
#输出为:
'''
输入一个数:a
引发异常: RuntimeError('No active exception to reraise')
'''
【例子18】
def get_age():
age = int(input('请输入年龄(1-140):'))
if age in range(1, 141):
return age
raise ZeroDivisionError # 故意抛出异常
try:
age = get_age()
print('用户输入的年龄是:', age)
except ZeroDivisionError as err: # 接收抛出的异常
print('用户输入的年龄不在1-140之间,获取年龄失败!')
#输出为:
'''
请输入年龄(1-140):0
用户输入的年龄不在1-140之间,获取年龄失败!
'''
当输入年龄不在[1~140]之间时,get_age()函数的raise就会抛出一个异常,然后except在接受在上面try代码块中get_age()函数中raise抛出的异常。