文章目录
8.1异常是什么
Python使用异常对象来表示异常状态,并在遇到错误时引发异常,异常对象未被处理(捕获)是,程序将终止并显示一条错误消息(traceback)
>>> 1 / 0
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ZeroDivisionError: integer division or modulo by zero
每个异常都是某个类的实例(这里是ZeroDivisionError类)
8.2让事情沿你指定的轨道出错
8.2.1raise语句
raise Exception及其子类,创建一个实例
>>>raise Exception('某种错误')
Exception Traceback (most recent call last)
<ipython-input-65-d585217b9620> in <module>()
----> 1 raise Exception('某种错误')
Exception: 某种错误
类名 | 描述 |
---|---|
Exception | 几乎所有的异常类都是从它派生而来的 |
AttributeError | 引用属性或给它赋值失败时引发 |
OSError | 操作系统不能执行指定的任务(如打开文件)时引发,有多个子类 |
IndexError | 使用序列中不存在的索引时引发,为LookupError的子类 |
KeyError | 使用映射中不存在的键时引发,为LookupError的子类 |
NameError | 找不到名称(变量)时引发 |
SyntaxError | 代码不正确时引发 |
TypeError | 将内置操作或函数用于类型不正确的对象时引发 |
ValueError | 将内置操作或函数用于这样的对象时引发:其类型正确但包含的值不合适 |
ZeroDivisionError | 在除法或求模运算的第二个参数为零时引发 |
8.2.2自定义的异常类
直接或间接地继承Exception
class SomeCustomException(Exception):
pass
8.3捕获异常
使用try:except:语句
>>>a=int(input())
>>>b=int(input())
>>>print(a/b)
9
0
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
<ipython-input-68-66cf9d13ffe6> in <module>()
1 a=int(input())
2 b=int(input())
----> 3 print(a/b)
ZeroDivisionError: division by zero
将报错的句子用try:except包围
>>>a=int(input())
>>>b=int(input())
>>>try:
>>> print(a/b)
>>>except ZeroDivisionError:
>>> print('除数不能为0')
8
0
除数不能为0
也可以用if语句检查
>>>a=int(input())
>>>b=int(input())
>>>if b==0:
>>> print('b can not be 0')
>>>else:
>>> print(a/b)
使用if语句检查错误,但是每次遇到都需要些一条if语句,如果使用try:excep语句,可以一大段可能报相同异常的语句包含在内
>>>a=int(input())
>>>b=int(input())
>>>c=int(input())
>>>try:
>>> print(a/b)
>>> print(55/c)
>>>except ZeroDivisionError:
>>> print('除数不能为0')
8
0
0
除数不能为0
8.3.1不用提供参数raise
捕获异常后,如果要重新引发它(即继续向上传播),可调用raise且不提供任何参数
class MuffledCalculator:
switch= False
def calc(self, expr):
try:
return eval(expr)
except ZeroDivisionError:
if self.switch:
print('Division by zero is illegal')
else:
raise
当try:except中间的内容有错,会执行except:之后的内容,而self.switch可以控制是执行print还是raise引发异常
>>>caculator=MuffledCalculator()
>>>caculator.switch=True #可以直接从外部如此控制switch,也可以直接在类中修改switch值
>>>caculator.calc('10/0')
Division by zero is illegal
这样就没有返回
如果希望在一个异常产生时触发另外一个异常,在except:后面使用raise语句
try:
1/0
except ZeroDivisionError :
raise ValueError
-----------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
<ipython-input-99-3b9569073fad> in <module>()
1 try:
----> 2 1/0
3 except ZeroDivisionError :
ZeroDivisionError: division by zero
During handling of the above exception, another exception occurred:
ValueError Traceback (most recent call last)
<ipython-input-99-3b9569073fad> in <module>()
2 1/0
3 except ZeroDivisionError :
----> 4 raise ValueError
ValueError:
用raise from None可以禁用上下文
try:
1/0
except ZeroDivisionError :
raise ValueError from None
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-101-fb5883754f6a> in <module>()
2 1/0
3 except ZeroDivisionError :
----> 4 raise ValueError from None
ValueError:
8.3.2多个except子句
如果一段代码可能出现多种错误,可以用多个异常来捕获
>>>a=int(input())
>>>b=int(input())
>>>c=input()
>>>try:
>>> print(a/b)
>>> print(a/c)
>>>except ZeroDivisionError:
>>> print('除数不能为0')
>>>except TypeError:
>>> print('除数必须是数字')
8
0
l
除数不能为0
Notice:这里如果有多种错误,那么只会报第一个错,也只会在显示捕获的第一个异常
8.3.3一箭双雕
如果想同时补捕获的所有异常,可以将这些异常放在一个元组中:
>>>a=int(input())
>>>b=int(input())
>>>c=input()
>>>try:
>>> print(a/b)
>>> print(a/c)
>>>except (ZeroDivisionError,TypeError):
>>> print('除数不能为0,除数必须是数字')
8
0
kk
除数不能为0,除数必须是数字
Notice:第二个异常只有在第一个异常没有出现时才会
8.3.4捕获对象as e
在except子句中访问异常对象本身
>>>a=int(input())
>>>b=int(input())
>>>c=input()
>>>try:
>>> print(a/b)
>>> print(a/c)
>>>except (ZeroDivisionError,TypeError) as e:#e就是异常对象本身,显示捕获
>>> print(e)
8
0
kk
division by zero
Notice:这里也只会打印出第一个报错对象
8.3.5一网打尽
在实际运行过程中,不是所有的异常都能被预期捕获,如果需要捕获所有的异常,可以使用try:except语句
>>>try:
>>> a=int(input())
>>> b=int(input())
>>> c=input()
>>> print(a/b)
>>> print(a/c)
>>>except :
>>> print('somewhere error')
1
5
p
0.2
somewhere error
但是这种做法会掩盖掉为预期到的错误,这还将捕获用户使用Ctrl + C终止执行的企图、调用函数sys.exit来终止执行的企图等。但是选择except Exception as e,会将类似sys.exit这样非Exception派生的异常正常报出。[SystemExit和KeyboardInterrupt,从BaseException(Exception的超类)派生而来的。]
>>>try:
>>> a=int(input())
>>> b=int(input())
>>> c=input()
>>> print(a/b)
>>> print(a/c)
>>>except Exception as e:
>>> print(e)
8.3.6万事大吉时else
在没有异常的时候执行一段代码,可以在try:exception后面添加一个else子句
>>>a=int(input())
>>>b=int(input())
>>>try:
>>> print(a/b)
>>>except:
>>> print('somewhere wrong')
>>>else:#如果没有异常
>>> print('everything ok')
8
4
2.0
everything ok
也可以用在循环里面
while True:
要执行的操作
如果没有异常:break
>>>while True:
>>> try:
>>> a=int(input())
>>> b=int(input())
>>> print(a/b)
>>> except:
>>> print('some error')
>>> else:
>>> break
5
0
some error # 开始下一个循环
5
1
5.0# 没有异常结束循环
但是try:except可能会将那些非Exception派生的异常也捕获,所以最好用except Exception as e,打印消息
>>>while True:
>>> try:
>>> a=int(input())
>>> b=int(input())
>>> print(a/b)
>>> except Exception as e:
>>> print('some error')
>>> print(e)
>>> else:
>>> break
5
kk
someerror
invalid literal for int() with base 10: 'kk'
5
0
someerror
division by zero
5
4
1.25
8.3.7finally
finally子句可用在发生异常是指向清理工作,try:finally
>>>b = None #赋初始值
>>>try:
>>> #b = None 这里也可赋初始值
>>> b = 1 / 0#由于1/0无解,所以实际上b不会被赋值,如不赋予初始值,则就不存在b,后面del b也会报错
>>>finally:
>>> print('Cleaning up ...')
>>> del b
Cleaning up ...
Traceback (most recent call last):
File "C:\python\div.py", line 4, in ?
x = 1 / 0
ZeroDivisionError: integer division or modulo by zero
该程序会在运行完finally子句之后报错
finally子句适合用于确保文件或网络套接字等得以关闭
也可以一条语句同时包含try、except、finally和else
>>>x = None
>>>try:
>>> x = 1 / 0
>>>except:
>>> print('some Error')
>>>else:
>>> print('everything ok')
>>>finally:
>>> print('Cleaning up ...')
>>> del x
some Error
Cleaning up ...
8.4异常和函数
如果不处理函数中引发的异常,它将向上传播到调用函数的地方。如果再那里也未得到处理,异常继续传播到主程序,未得到处理,程序将终止并显示栈跟踪消息
>>> def faulty():
... raise Exception('Something is wrong')
...
>>> def ignore_exception():
... faulty()
...
>>> def handle_exception():
... try:
... faulty()
... except:
... print('Exception handled')
...
>>> ignore_exception()
Traceback (most recent call last):
File '<stdin>', line 1, in ?
File '<stdin>', line 2, in ignore_exception
File '<stdin>', line 2, in faulty
Exception: Something is wrong
>>> handle_exception()
Exception handled
8.5异常之禅
try:except比if:else语句在处理异常时效率更高
>>>def describe(person):
>>> print(person['name'])
>>> print(person['age'])
>>> if 'sex' in person:
>>> print(person['sex'])
>>>p={'name':'jack','age':24,'sex':'female'}
>>>describe(p)
jack
24
female
if这样查找sex键需要查找两次
>>>def describe(person):
>>> print(person['name'])
>>> print(person['age'])
>>> try:
>>> print(person['sex'])
>>> except KeyError:
>>> pass
>>>p={'name':'jack','age':24,'sex':'female'}
>>>describe(p)
jack
24
female
这里直接假设sex键存在,不存在就引发异常
因此try:except用于检查对象是否包含某个属性也很有用
>>>class Student():
>>> grade='junior'
>>> def name(self,name):
>>> self.name=name
>>> def get_info(self):
>>> print(self.name,self.grade)
>>>s=Student()
>>>s.name('jack')
>>>try:
>>> s.name
>>>except Exception as e:
>>> print(e)
>>>else:
>>> print('it has the attribute')
8.6不那么异常的情况
如果只想发出警告,指出情况偏离了正轨,可使用模块warnings中的函数warn。
引发警告
>>>from warnings import warn
>>>warn('something wrong')
D:\ProgramData\Anaconda3\lib\site-packages\ipykernel_launcher.py:2: UserWarning: something wrong
异常或者说错误error是警告的一种类别,其他类别还有ignore等,可以用filterwarnings筛选出来后定义警告信息
>>>from warnings import filterwarnings
>>>filterwarnings('ignore')
>>>warn('hhhh')
>>>filterwarnings('error')
>>>warn('sssss')
---------------------------------------------------------------------------
UserWarning Traceback (most recent call last)
<ipython-input-12-1e8124bc9f52> in <module>()
3 warn('hhhh')
4 filterwarnings('error')
----> 5 warn('sssss')
UserWarning: sssss