0x01:异常
程序在运行过程中,由于我们的编码不规范,或者其他原因一些客观原因,导致我们的程序无法继续运行,此时,程序就会出现异常。如果我们不对异常进行处理,程序可能会由于异常直接中断掉。为了保证程序的健壮性,我们在程序设计里提出了异常处理这个概念。
异常: Python 使用异常对象来代表异常状态,并在遇到错误时引发异常。异常对象未被处理(或捕获时),程序将终止并显示一条错误信息(traceback)。
>>> 1 / 0
Traceback (most recent call last):
File "<pyshell#0>", line 1, in <module>
1 / 0
ZeroDivisionError: division by zero
>>>
如果异常只能用来显示错误信息,就没有什么意思了。每个异常都是某个类(这里是ZeroDivisionError)的实例。你能以各种方式引发和捕获这些实例,从而逮住错误并采取措施。
0x02: 一些内置的异常类
Exception: 几乎所有的异常类都是它派生出来的
AttributeError: 引用属性或给它赋值失败时引发
OSError: 操作系统不能执行指定的任务(如打开文件)时引发,有多个子类
IndexError: 使用序列中不存在的索引时引发,为LookupError的子类
KeyError: 使用映射中不存在的键时引发,为LookupError的子类
NameError: 找不到名称(变量)时引发
SyntaxError: 代码不正确时引发
TypeError: 将内置操作或函数用于类型不正确的对象时引发
ValueError: 将内置操作或函数用于这样的对象时引发:其类型正确,但是包含的值不正确
ZeroDivisionError:在除法或求模运算的第二个参数为零时引发
创建自定义的异常类
class SomeCustomException(Exception): pass
0x03: 捕获异常
异常比较有趣的地方是可以对其进行处理,通常称之为捕获异常。可以使用 try / except 语句。
try:
可能会出现异常的代码块
except 异常的类型:
出现异常以后的处理语句
示例:
>>> try:
x = int(input('Enter the first number: '))
y = int(input('Enter the second number: '))
print(x / y)
except ZeroDivisionError:
print(" The second number can't be zero")
Enter the first number: 1
Enter the second number: 0
The second number can't be zero
>>>
(1) 不用提供参数:
捕获异常后,如果要重新引发它(即继续向上传播),可以调用 raise 且不提供任何参数(也可以显示的提供捕获到的异常)。
接下来看一个能够“抑制” 异常 ZeroDivisionError的计算器类。如果启动了这种功能,计算器将打印一条错误信息,而不让异常继续传播。在与用户进行交互式的会话中,使用这个功能很有用;但是在程序内部使用,引发异常是最好的选择。(此时应该关闭抑制功能)
>>> class MuffledCalculator:
muffed = False 关闭抑制功能
def calc(self,expr):
try:
return eval(expr)
except ZeroDivisionError:
if self.muffled:
print('Division by zero is illegal')
else:
raise
>>>
注意:发生除零的行为时,如果启用了抑制功能,方法calc将隐式的返回 none。换而言之,如果启用了印制功能,就不应依赖返回值。
>>> calculation = MuffledCalculator()
>>> calculation.calc('10/2')
5.0
关闭抑制功能
>>> calculation.calc('10/0')
Traceback (most recent call last):
File "<pyshell#10>", line 5, in calc
return eval(expr)
File "<string>", line 1, in <module>
ZeroDivisionError: division by zero
开启抑制功能
>>> calculation.muffled = True
>>> calculation.calc('10/0')
Division by zero is illegal
>>>
关闭了抑制功能,捕获了异常ZeroDivisionError,但是继续向上传播它。
如果无法处理异常,在except子句中使用不带参数的 raise 通常是不错的,但有时你可能想引发别的异常。在这种情况下,导致进入except子句的异常被作为异常上下文存储起来,并出现在最终的错误消息中,可以使用 raise … from … 语句来提供自己的异常上下文,也可以使用None来禁用上下文。
>>> try:
1/0
except ZeroDivisionError:
raise ValueError
Traceback (most recent call last):
File "<pyshell#94>", line 2, in <module>
1/0
ZeroDivisionError: division by zero
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<pyshell#94>", line 4, in <module>
raise ValueError
ValueError
>>> try:
1/0
except ZeroDivisionError:
raise ValueError from None
Traceback (most recent call last):
File "<pyshell#70>", line 4, in <module>
raise ValueError from None
ValueError
>>>
(2) 多个except 子句:
>>> try:
x = int(input('Enter the first number: '))
y = int(input('Enter the second number: '))
print(x / y)
except ZeroDivisionError:
print("the second number can't be zero")
except ValueError:
print("That wasn't a number, was it ")
Enter the first number: 10
Enter the second number: "hello"
That wasn't a number, was it
(3) 一箭双雕:
如果使用一个except 子句捕获异常,可以在一个元组中指定这些异常,如下所示:
>>> try:
x = int(input('Enter the first number: '))
y = int(input('Enter the second number: '))
print(x / y)
except (ZeroDivisionError,TypeError,ValueError,NameError):
print("your numbers were bogus ..")
Enter the first number: 10
Enter the second number: "hell"
your numbers were bogus ..
(4)捕获对象:
要在except子句中访问异常对象本身,可以使用两个而不是一个参数。(即便你在捕获多个异常时,也只向except提供了一个参数——元组)需要让程序继续运行并记录错误,指向用户显示。
>>> try:
x = int(input('Enter the first number: '))
y = int(input('Enter the second number: '))
print(x / y)
except (ZeroDivisionError,TypeError,ValueError,NameError) as a:
print(a)
Enter the first number: 10
Enter the second number: "hello"
invalid literal for int() with base 10: '"hello"'
(5)一网打尽:
即使你处理了好几种异常,但可能还有一些漏网之鱼,例如:用户在不提示任何输入内容就按回车键,将出现一条错误信息,还有一些相关问题出在什么地方(栈跟踪)。
如果要使用一段代码捕获所有的异常,只需在except语句中不指定给任何类即可:
try:
x = int(input('Enter the first number: '))
y = int(input('Enter the second number: '))
print(x / y)
except :
print("something wrong happend")
(6) 异常循环:
>>> while True:
try:
x = int(input('Enter the first number: '))
y = int(input('Enter the second number: '))
value = x / y
print('x / y is',value)
except Exception as e:
print('Invalid inputL',e)
print('Please try again')
else:
break
(7)try..finally语句
try…finally…语句用来表达这样的情况:
在程序中,如果一个段代码必须要执行,即无论异常是否产生都要执行,那么此时就需要使用finally。 比如文件关闭,释放锁,把数据库连接返还给连接池等。
try:
f = open('test.txt')
try:
while True:
content = f.readline()
if len(content) == 0:
break
print(content)
except:
#如果在读取文件的过程中,产生了异常,那么就会捕获到
#比如 按下了 ctrl+c
pass
finally:
f.close()
print('关闭文件')
except:
print("没有这个文件")
说明:
我们可以观察到KeyboardInterrupt异常被触发,程序退出。但是在程序退出之前,finally从句仍然被执行,把文件关闭。
0x04:异常和函数
异常和函数有着天然的联系。如果不处理函数中引发的异常,它将向上传播到调用函数的地方中。如果在那里也没有得到处理,异常将继续传播,直至达到主程序(全局作用域)。
如果主程序没有异常处理程序,程序将终止并显示站跟踪消息。
def faulty():
raise Exception('Something is wrong')
def ingore_exception():
faulty()
def handle_exception():
try:
faulty()
except:
warnings.filterwarnings(action,category=warning,...) 用于过滤警告
warnings.warn(message,category=None) 用于发出警告
如果你只想发出警告,指出情况偏离了正轨,可以使用模块 warnings中的函数 warn。
警告只显示一次,如果在运行最后一行代码,什么事情也不会发生。
>>> from warnings import warn
>>> warn("I've got a bad feeling about this. ")
Warning (from warnings module):
File "<pyshell#198>", line 1
UserWarning: I've got a bad feeling about this.
>>> warn("I've got a bad feeling about this. ")
>>>
如果其他代码在使用你的模块,可以使用模块 warnings中的函数 filterwarnings 来抑制你发出的警告(或特定类型的警告),并指出采取的措施,如:”error“或”ignore“。
>>> from warnings import filterwarnings
>>> filterwarnings('ignore')
>>> warn("Anyone out there?")
>>> filterwarnings("error")
>>> warn("something is very wrong")
Traceback (most recent call last):
File "<pyshell#210>", line 1, in <module>
warn("something is very wrong")
UserWarning: something is very wrong
>>>