python:异常处理

 

什么是异常:

1、一般情况下,Python无法正常处理程序时就会发生一个异常。异常是Python对象,表示一个错误。当Python脚本发生异常时我们需要捕获处理它,否则程序会终止执行。

2、异常即是一个事件,该事件会在程序执行过程中发生,影响了程序的正常执行。每一个异常都是一些类的实例,这些实例可以被引用,并且可以用很多种方法进行捕捉,使得错误可以被处理,而不是让整个程序失败


异常处理:捕捉异常可以使用try/except语句

try / except语句用来检测try语句块中的错误,从而让except语句捕获异常信息并处理。如果你不想在异常发生时结束你的程序,只需在try里捕获它。
语法:
以下为简单的try....except...else的语法:

try:
<语句>                   #运行的代码(需要检测的语句)

except <名字>:          #发生异常时的操作。名字为对应错误的名称
<语句>  

1、try的工作原理是,当开始一个try语句后,python就在当前程序的上下文中作标记,这样当异常出现时就可以回到做标记的地方。

2、try子句先执行,接下来会发生什么依赖于执行时是否出现异常。如果当try后的语句执行时发生异常,python就跳回到try并执行第一个匹配该异常的except子句每一个try,都必须至少有一个except。异常处理完毕,控制流就通过整个try语句(除非在处理异常时又引发新的异常)。

3、如果在try后的语句里发生了异常,却没有匹配的except子句,异常将被递交到上层的try,或者到程序的最上层(这样将结束程序,并打印缺省的出错信息)。

4、如果在try子句执行时没有发生异常,python将执行else语句后的语句(如果有else的话),然后控制流通过整个try语句。

5、当我们认为某些代码可能会出错时,就可以用try来运行这段代码,如果执行出错,则后续代码不会继续执行,而是直接跳转至错误处理代码,即except语句块,执行完except后,如果有finally语句块,则执行finally语句块

6、在try程序段中,我们放入容易犯错的部分。我们可以跟上except,来说明如果在try部分的语句发生错误时,程序该做的事情。如果没有发生异常,则except部分被跳过
例1:

def func(x,y):
    try:
        a = x/y
        print("a = ",a)
    except BaseException:
        print("程序异常,异常信息:被除数为0")

    else:
        a = x * y
        print("a = ",a)


func(2,2)

"""
上面代码的输出结果为:
a =  1.0
a =  4
"""

例1_1:

def func(x,y):
    try:
        a = x/y
        print("a = ",a)
    except BaseException:       #提前获得异常,使得程序不会报错
        print("程序异常,异常信息:被除数为0")

func(2,0)

#上面代码的输出结果为:程序异常,异常信息:被除数为0

在上面两个例子中,如果在做除法前对y值进行判断,也可以解决问题。但是如果给程序加入更多除法,就得给每个除法语句加一个判断语句,这样整个代码看上去就是一堆类似if的功能重复判断语句,真正有效的代码没有多少。而是用try/except只需要一个错误处理器即可。
 

捕获多个异常:

try:
<语句>        #运行别的代码

except <名字>:
<语句>        #如果在try部份引发了'name1'异常

except <名字>,<数据>:
<语句>         #如果引发了'name2'异常,获得附加的数据

例4:

def mult_exception(x, y):
    try:
        a = x / y

    except ZeroDivisionError:
        print("this is ZeroDivisionError")

    except NameError:
        print("this is NameError")

mult_exception(2, 0)

#上面代码的输出结果为:this is ZeroDivisionError

上面代码的执行步骤为:
首先执行try子句(在关键字try和except之间的语句)。如果没有发生异常,则忽略except字句,try子句执行后结束。如果在执行try字句过程中发生异常,try字句余下部分就会被忽略,如果异常的类型和except之后的名称相符,对应的except字句就会被执行。最后执行try之后的代码。一个try语句可能包含多个except字句,分别处理不同的异常,但最多只有一个分支会被执行
 

使用一个块捕捉多个异常:

如果需要使用一个块捕捉多个类型异常,可以将他们作为元组列出。使用该方式时,遇到的异常类型是元组中的任意一个,都会走异常流程

例5:

def mult_exception(x,y):
    try:
        a = x/y
        print(a)

    except (ZeroDivisionError,NameError,TypeError):
        print("错误类型为其中一个:ZeroDivisionError,NameError,TypeError")

mult_exception(2,0)

#上面代码的输出结果为:错误类型为其中一个:ZeroDivisionError,NameError,TypeError

这样做的好处是:假如我们希望多个except字句输出同样的信息,就没有必要在几个except字句中重复输入语句,放到一个异常块中即可

捕捉对象:

如果希望在except字句中访问异常对象本身,也就是看到一个异常对象真正的异常信息,而不是输出自己定义的异常信息,可以使用as e的形式,我们称之为捕捉异常

例6:

def mult_exception(x,y):
    try:
        b = name   #错误类型: name 'name' is not defined
        a = x/y    #错误类型: division by zero
        print(a)

    except (ZeroDivisionError,NameError,TypeError) as e:
        print("错误类型:",e)

mult_exception(2,0)

#上面的代码输出结果为:错误类型: name 'name' is not defined
#有多个异常同事存在时,在执行最前面那个后,不会执行后面的

注:在python2中,捕捉对象的写法是"Exception,e",中间使用","分隔,而不是用as,此处的e也可以使用其他字母,用e意义比较明确,取自except的第一个字母
 

全捕捉:

例7:

def mult_exception(x,y):
    try:

        a = x/y

    except (ZeroDivisionError,NameError,TypeError) as e:
        print(e)

mult_exception(2,"n")

#上面的代码输出结果为:unsupported operand type(s) for /: 'int' and 'str'

由上面例子可以看出即使程序能自动处理很多种异常,但是还是会有部分异常会被遗漏(逃过了try\except的检查)。在这种情况下,与其使用异常捕捉的try\except语句隐藏异常,还不如让程序立即崩溃

例7_1:

def mult_exception(x,y):
    try:
        b = name
        a = x/y

    except:
        print("Error happend")

mult_exception(2,"nn")

#上面的代码输出结果为:Error happend

1、由上面例子可以看出,可以在except子句中忽略所有异常类,从而让程序输出自己定义的异常信息

2、从实用性方面来讲,不建议这么做,因为这样会隐藏所有的的异常,从而不好找到问题。建议使用抛出异常的方式处理
 

异常中的else:
 

try:
<语句>        #运行别的代码

except <名字>:
<语句>        #如果在try部份引发了'name1'异常

except <名字>,<数据>:
<语句>        #如果引发了'name2'异常,获得附加的数据

else:
<语句>        #如果没有异常发生

例8:

def mult_exception(x,y):
    try:

        a = x/y
        print(a)     
    except:
        print("Error happend")

    else:
        print("我也被执行了")

mult_exception(2,1)

"""
上面的代码输出结果为:
2.0
我也被执行了
"""

finally子句:

1、python中的finally子句需要和try子句一起使用,组成try / finally的语句形式,try / finally语句表示无论发生异常与否都将执行最后的代码

2、try / finally语句也可以和except、else一起组合使用。但要记得else在except之后,finally在except和else之后(这种组合中except的作用为:用于截获可能发生的异常,使得程序不报错而执行except所属语句)

例9:

def mult_exception(x,y):
    try:

        a = x/y
        print(a)
    except ZeroDivisionError:
        print("some bad things is happend:division by zero")

    else:
        print("没有异常时才执行我")

    finally:
        print("不管怎样我都要被执行")
mult_exception(2,1)

"""
上面的代码输出结果为:
2.0
没有异常时才执行我
不管怎样我都要被执行
"""

例9_1:

def mult_exception(x,y):
    try:

        a = x/y
        print(a)
    except ZeroDivisionError:
        print("some bad things is happend:division by zero")

    else:
        print("没有异常时才执行我")

    finally:
        print("不管怎样我都要被执行")
mult_exception(2,0)

"""
上面的代码输出结果为:
some bad things is happend:division by zero
不管怎样我都要被执行
"""

抛出异常:

1、如果只想知道是否抛出了异常,并不想处理,使用一个简单的raise语句就可以再次把异常抛出。一旦执行了raise语句,raise后面的语句将不能执行。 

2、用raise语句来引发一个异常。异常/错误对象必须有一个名字,且它们应是Error或Exception类的子类

3、raise关键字后面是抛出是一个通用的异常类型(Exception),一般来说抛出的异常越详细越好,Python在exceptions模块内建了很多的异常类型,通过使用dir函数来查看exceptions中的异常类型

4、raise关键字后面需要指定你抛出的异常类型为了能够捕获异常,"except"语句必须有用相同的异常来抛出类对象或者字符串。

raise语法格式如下:

raise [Exception [, args [, traceback]]]

1、语句中 Exception 是异常的类型(例如,NameError)参数标准异常中任一种,args 是自已提供的异常参数。最后一个参数是可选的(在实践中很少使用),如果存在,是跟踪异常对象。

2、格式:raise 异常名称(‘异常描述’)

例10:

def func(s):
    try:

        if s is None:
            print("s 是空对象")
            raise NameError("传入的列表为None")     
        print(len(s))                         #未触发NameError,因此正常执行该行      
    except TypeError:
        print("空对象没有长度")

list = []
func(list)

例10_1:

def func(s):
    try:

        if s is None:
            print("s 是空对象")
            raise NameError("传入的列表为None")     # 如果引发NameError异常,后面的代码将不能执行
        print(len(s))                               # 这句不会执行,但是后面的except还是会走到
    except TypeError:
        print("空对象没有长度")

list = None
func(list)

"""
上面代码的输出结果为:
s 是空对象
Traceback (most recent call last):
  File "F:/Pycharm_project/Demo/demo/Exercise_2.py", line 12, in <module>
    func(list)
  File "F:/Pycharm_project/Demo/demo/Exercise_2.py", line 6, in func
    raise NameError("传入的列表为None")     # 如果引发NameError异常,后面的代码将不能执行
NameError: 传入的列表为None
"""

例10_2:

filename = input('please input file name:')

if filename=='hello':
    raise NameError('input file name error !')

"""
上面代码的输出结果为:
Traceback (most recent call last):
  File "F:/Pycharm_project/Demo/demo/Exercise.py", line 4, in <module>
    raise NameError('input file name error !')
NameError: input file name error !
"""

程序要求用户输入一个文件名,如果用户输入的文件名是hello ,那么抛出一个NameError的异常(自己手动抛出一个异常),用户输入hello 和NameError异常之间没有任何必然联系,我只是认为通过raise来这样定义,我当然也可以定义称TypeError ,但我定义的异常类型必须是python提供的。
 

例10_3:

def mye( level ):
    if level < 1:
        raise Exception("Invalid level!")
        # 触发异常后,后面的代码就不会再执行
try:
    mye(0)            # 触发异常
except Exception as e:  #通过except来捕获异常
    print(1,e)
else:
    print(2)

#上面代码的输出结果为:1 Invalid level!

传递异常:

当你捕获到异常之后又希望再次的触发异常只需要使用不带任何参数的raise关键字

例13:

在阅读开源代码时,经常能看到,单独一个raise的使用,比如:

try:
	do something
except IOError:
	raise

这个是把最近一次产生的异常重新抛出来,交给上层 (我已经知道了这个异常并且捕获到了,但是我不处理,而由我的上层调用处理)

try:
    try:
        raise IOError
    except IOError:
        print("inner exception")
        raise 
except IOError:
    print("outter exception")


"""
上面代码的输出结果为:
inner exception
outter exception
"""
import os
try:
    openFile = open('notExistsFile.txt','r')
    fileContent = openFile.readlines()
except IOError:
    print('File not Exists')
    if not os.path.exists('notExistsFile.txt'):
        raise
except:
    print('process exception')

1、异常会在捕获之后再次触发同一个异常。

2、如果要捕获异常后要重复抛出,请使用raise,后面不要带任何参数或信息

3、except语句不是必须的,finally语句也不是必须的,但是二者必须要有一个,否则就没有try的意义了。
 

自定义异常:

1、python允许程序员自定义异常,用于描述python中没有涉及的异常情况,自定义异常必须继承Exception类,自定义异常按照命名规范以"Error"结尾,显示地告诉程序员这是异常。自定义异常使用raise语句引发,而且只能通过人工方式触发。

2、因为错误就是类捕获一个错误就是捕获该类的一个实例,因此错误并不是凭空产生的,而是由一些不合理的部分导致的。python的内置函数会抛出很多类型的错误,我们自己编写的函数也可以抛出错误。如果要抛出错误,那么可以根据需要定义一个错误的类,选择好继承关系,然后用raise语句抛出一个错误的实例

例11:

class MyError(Exception):
    def __init__(self):
        pass
    def __str__(self):
        return "这是一个自定义的错误类型"

def MyErrorTset():
    try:
        raise MyError()
    except MyError as error:             #except MyError:
        print("exception info: ",error)  #print("exception info: ",MyError())

MyErrorTset()

#上面代码的输出结果为:exception info:  这是一个自定义的错误类型
#这种写法的结果是:如果多个代码调用这个错误类,得到的错误信息是一样的

例11_1:

class CustomError(Exception):
    def __init__(self,ErrorInfo):
        super().__init__(self)      #初始化父类
        self.errorinfo = ErrorInfo
    def __str__(self):
        return self.errorinfo

if __name__ == '__main__':
    try:
        raise CustomError('客户异常')
    except CustomError as e:
        print(e)

#上面代码的输出结果为:客户异常
#这种写法的结果是:在执行具体某个代码的可以再次自定义错误信息(不同代码调用时,得到的错误信息不一样)

异常的函数:

如果异常在函数内引发而不被处理,就会传播至函数调用的地方。如果异常在调用的地方还是没有被处理,则会继续传播,一直到达主程序。如果在主程序也没有做异常处理,异常就会被python解释器捕获,输出一个错误信息,然后退出程序

例子12:

def func(x,y):
    return x/int(y)

def func_1(x,y):
    return func(x,y)*10

def main(x,y):
    return func_1(x,y)

main(2,0)

"""
上面代码的输出结果为:
Traceback (most recent call last):
  File "F:/Pycharm_project/Demo/demo/Exerciser_3.py", line 10, in <module>
    main(2,0)
  File "F:/Pycharm_project/Demo/demo/Exerciser_3.py", line 8, in main
    return func_1(x,y)
  File "F:/Pycharm_project/Demo/demo/Exerciser_3.py", line 5, in func_1
    return func(x,y)*10
  File "F:/Pycharm_project/Demo/demo/Exerciser_3.py", line 2, in func
    return x/int(y)
ZeroDivisionError: division by zero
"""

从上面的例子可以看出,func函数中产生的异常通过函数func_1和main传播,在传播过程中都没有对异常进行处理,因此最后抛出堆栈的异常信息。

assert语句触发异常:

assert语句根据后面的表达式的真假来控制程序流。若为True,则往下执行。若为False,则中断程序并调用默认的异常处理器,同时输出指定的提示信息。 
格式:

assert expression,'information'

例14:

def testAssert(x):
    assert x < 1,'Invalid value'
testAssert(1)

print 'Valid value'

#上面代码的输出结果为:Valid value

 

备注:

异常信息是以堆栈的形式被抛出的,因为是从下往上看的。所谓堆栈就是最先被发现的异常信息最后被输出(就像子弹入弹夹和出弹夹一样),也被称作先进后出

猜你喜欢

转载自blog.csdn.net/qq_39314932/article/details/81433060