[Python]第八章 异常

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

猜你喜欢

转载自blog.csdn.net/weixin_40844116/article/details/84393274