Python(9)——Python中的异常处理


目录:

Python中的异常处理语句

Python中常见的异常类型

自定义异常


Python中finallywith关键字的使用

一:Python中异常处理的语句

在程序执行的时候,可能因为种种原因导致程序无法继续运行,从而出现异常。如果我们不对异常进行处理的话,程序会由于异常而导致中断。为了保证程序具有良好的健壮性和体验性,在程序设计里提出了异常处理概念。
先来看一下不使用异常处理时的程序:

while True :
    num1 = int(input('请输入一个数字:'))
    num2 = int(input('请输入一个数字:'))
    print('两个数字相除等于{}'.format(num1/num2))

这是一个简易的求两数相除的代码,正常情况下,while True语句会不断的运行。但是如果出现了除数为零时则会出现异常,并中断了这个循环,代码如下:

while True :
    num1 = int(input('请输入一个数字:'))
    num2 = int(input('请输入一个数字:'))
    print('两个数字相除等于{}'.format(num1/num2))
# 打印结果如下:
请输入一个数字:1
请输入一个数字:0
 # 错误结果如下:
ZeroDivisionError: division by zero

假设这是一个已经发布的程序(笑),用户在使用不小心输入了除数是0后,则会出现异常并且导致程序的中断,对于用户来说,这是非常糟糕的体验。

开发程序其实就像预测天气一样。即使是代码的异常错误,也应该能预测且被控制。

好在Python如同大多数编程语言一样,支持异常处理。

Python异常处理:

Python中异常处理的语句是:try…except

在try里面放入要运行的代码块,except里面写入出现错误时的处理操作,代码如下:

while True :
    num1 = int(input('请输入一个数字:')) # 通过int类将input接收的str类型转为int类型
    num2 = int(input('请输入一个数字:'))
    try: # 将运行的代码放入try里
        division = num1 / num2
    except Exception: # 产生错误时运行的代码,Exception是一个类
        print('程序出现了未知故障')
    else: # 未产生错误则执行
        print('两个数字相除等于{}'.format(num1 / num2)) # 当未出现异常时则执行此代码# 打印结果如下:
请输入一个数字:1
请输入一个数字:0
程序出现了未知故障 # 因为把0作为了除数,所以执行了except中的代码
请输入一个数字:2
请输入一个数字:1
两个数字相除等于2.0
......

使用了try…except关键字后即使将0作为了除数,程序不仅没有奔溃,反而运行了之前预测错误时编写的代码。这就使得这个程序代码具有非常好的用户体验。
但是上面这段代码还有一个需要优化的地方,那就是如果用户输入的不是数字时,int类在执行转换的时候会出现异常,则会终止该程序。
可以将input代码段移动到try中,当输入出现异常时也对它进行捕获,代码如下:

while True :
    try: # 将运行输入的input的代码放入try里
        num1 = int(input('请输入一个数字:'))
        num2 = int(input('请输入一个数字:'))
        division = num1 / num2
    except Exception: # 产生错误时运行的代码
        print('程序出现了未知故障')
    else: # 未产生错误则执行
        print('两个数字相除等于{}'.format(num1 / num2))

既然异常可以被捕获,那么也就可以对异常进行区分,使用多个except语句,处理不同的异常,如下:

while True :try: # 将运行的代码放入try里
        num1 = int(input('请输入一个数字:'))
        num2 = int(input('请输入一个数字:'))
        division = num1 / num2
    except ZeroDivisionError: # 当出现除数是零时运行下面代码
        print('程序出现了除以零错误')
    except ValueError: # 当程序出现输入类型错误时运行下面代码
        print('程序输入类型错误')
    else: # 未产生错误则执行
        print('两个数字相除等于{}'.format(num1 / num2))# 打印结果如下:
请输入一个数字:1
请输入一个数字:0
程序出现了除以零错误
请输入一个数字:1
请输入一个数字:hi
程序输入类型错误

也可以使用一个except语句对多个错误类型进行区分,代码如下:

while True :try: # 将运行的代码放入try里
        num1 = int(input('请输入一个数字:'))
        num2 = int(input('请输入一个数字:'))
        division = num1 / num2
    except (ZeroDivisionError,ValueError) as e : # 产生ZerDivisionError和ValueError错误时将捕获,并且保存在变量e中
        if isinstance(e,ZeroDivisionError):  # 判断e属于什么类的实例
            print('程序出现了除以零错误')
        elif isinstance(e,ValueError):
            print('程序输入类型错误')
    else: # 未产生错误则执行
        print('两个数字相除等于{}'.format(num1 / num2))
# 打印结果如下:
请输入一个数字:1
请输入一个数字:0
程序出现了除以零错误
请输入一个数字:hello
程序输入类型错误

使用except可以分别捕获错误类型,也可以直接捕获Exception类,因为所有的错误类都直接或间接的继承了该类。

二:常见的错误类型

ValueError:当数据类型错误时产生的错误类,代码如下

int ( 'hi' )
# 错误结果如下:
ValueError: invalid literal for int() with base 10: 'hi'

ZeroDivisionError:当除数为0时的产生的错误类,代码如下
2 / 0
# 错误结果如下:
ZeroDivisionError: division by zero

KeyError:当查询无效的字典key时产生的错误类,代码如下
a = { 'name' : '小王' , 'age' : 11 }
a['address']
# 错误结果如下:
KeyError: 'address'

IndexError:当查询无效的下标时产生的错误类,代码如下
a = [ 1,2,3,4,5]
a[6]
#错误结果如下:
IndexError: list index out of range

AttributeError:属性错误时产生的错误类,代码如下
a = 'hello'
a.list()
# 错误打印如下:
AttributeError: 'str' object has no attribute 'list'

ModuleNotFoundError:当导入了无效的模块时产生的错误类,代码如下
import  a
# 错误打印如下:
ModuleNotFoundError: No module named 'a'

NameError:使用了未定义的变量产生的错误,代码如下
a = 'hello'
print(b)
# 错误打印如下:
NameError: name 'b' is not defined

SyntaxError:出现了语法错误时产生的错误类型,代码如下
a = 'hello'
print( b
# 错误打印如下:
SyntaxError: unexpected EOF while parsing

FileExisError:当创建已存在的文件或文件夹时产生的错误类型,代码如下
import  os
os.mkdir('ABC') # 创建ABC文件夹
# 错误打印如下:
FileExistsError: [WinError 183] 当文件已存在时,无法创建该文件

FileNotFoundError:当使用了不存在的文件时产生的错误类型,代码如下
import  os
open('abc.txt',mode='rt') # 打开了不存在的abc.txt文件
# 错误打印如下:
FileNotFoundError: [Errno 2] No such file or directory: 'abc.txt'

ImportError:导入模块中错误的方法时产生的错误类,代码如下
from os import  opn # os模块中并没有opn类
open('abc.txt',mode='rt')
# 错误打印如下:
ImportError: cannot import name 'opn' from 'os' 

RecursionEroor:当内存地址溢出时产生的错误类,代码如下
def a(): # 简单的递归函数
    a()
​
a() # 调用函数a
# 错误打印如下:
RecursionError: maximum recursion depth exceeded
(StopIteration:用来停止迭代时产生的错误类。)

TypeError:类型错误时产生的错误类,代码如下
'hello' - 2 # 字符串与数值之间不支持“—”操作
# 错误打印如下:
TypeError: unsupported operand type(s) for -: 'str' and 'int'

三:自定义异常

可以针对自定义错误类型使用raise关键字抛出指定的异常,代码如下:

class UserLensError(BaseException):
    def __init__(self,lens):
        self.lens = lens
    def __str__(self): # 重写__str__方法,因为抛出错误时会执行此方法
       return ('用户名长度必须大于{}'.format(self.lens))def demo():
    User = input('请输入用户名')
    if len(User) < 4 :
        raise UserLensError(4) # 使用raise抛出异常
    else:
        return  User
​
demo()
# 抛出的错误如下:
raise UserLensError(4)
__main__.UserLensError: 用户名长度必须大于4
四:finally和with关键字

finally关键字:

finally关键字表示即使在错误抛出的时候,finally关键字内的代码依然会执行。代码如下:

def NT(a):
    try:
        a = int(a) # 将类型转为int
    except (Exception) as b: # 如果出现了错误则捕捉并存储在b变量中
        print('出现了错误')
    else:
        return  a  # 如果类型转换成功则返回a的值
    finally: # finally中代码都会被执行
        print('即使出现了错误,finally还是运行了')
​
NT('hi') # 将一个字符串转为int时报错
# 打印结果如下:
出现了错误
即使出现了错误,finally还是运行了

with关键字:

对于系统资源文件等,打开这些资源并执行完操作后,必须做的一件事就是要关闭该资源。因为系统允许打开的文件数量是有限制的,如果不及时关闭,会导致系统其他进程无法使用该资源。

以文件资源为例,文件的正常打开代码如下:

file = open ( 'C\text.txt' , 'r' ) # 正常以只读的方式打开
file.read( ) # 进行读操作
file.close( ) # 关闭该文件

上面的代码看起来是没有问题的,但是有一个隐藏的风险。那就是如果在进行读操作时候出现了未知错误导致下面的关闭文件代码未执行,这会对后面该文件的操作带来巨大的风险,这是要规避的。

使用with关键字的好处是再执行完对该资源的使用后,之后直接自动关闭该资源。保证了该资源的安全性。以使用文件资源为例,代码如下:

with open('text.txt', 'r') as f: # 打开text.txt文件,以只读的方法,并且存储到f中
    f.read()
print( "文件已经关闭" ) # 当执行到print语句时,with关键字已经自动关闭了text.txt文件

with关键字本质上是一个上下文管理器。

with后面的对象必须要有__enter__和__exit__方法,因为当进入到with代码块的时候会自动调用被with关键字打开对象的__enter__方法,当退出with代码块时,会自动调用对象__exit__方法。

只所以上面的with后面open函数能够执行,是因为open函数返回了一个TextIOWrapper类的对象,而TextIOWrapper类继承了TextIOBase类,而TextIOBase类又继承了IOBase类。在IOBase类中有__enter__和__exit__方法,所以with函数在执行open函数时,会调用IObase类中的__enter__和__exit__方法。

可以创建一个自定义的对象在with语句后面,只要这个对象有__enter__和__exit__方法即可,代码如下:

class test(object): # 创建一个自定义test类
    def __init__(self,name,age): # 初始化属性
        self.name = name
        self.age = age
​
    def __enter__(self): # with会首先调用对象的__enter__方法
        print('__enter__方法调用了')
        return  self # 这里用来查看self接收的对象,用来侧面反映with是先调用对象的__enter__方法后调用__exit__方法
    def __exit__(self, exc_type, exc_val, exc_tb): # 退出时则调用__exit__方法
        print('__exit__方法调用了')
​
a = test('laowang', 19) # 根据test类创建了一个实例对象
with a as b: # b里里面的数据是__enter__的返回值
    print(b)
# 打印结果如下:
__enter__方法调用了
<__main__.test object at 0x0000000001DCAAC8> # self接收的对象
__exit__方法调用了 # 可以见的是在with语句中的代码块执行完毕后才调用__exit__方法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值