python深入之异常和错误处理(重点)

关于错误和异常

(1)概念:错误是无法通过其他代码进行处理问题,如语法错误和逻辑错误,语法错误是单词或格式等写错,只能根据系统提示去修改相应的代码,逻辑错误是代码实现功能的逻辑有问题,系统不会报错,也是只能找到相应的代码进行修改;异常是程序执行过程中,出现的未知问题,这里语法和逻辑都是正确的,可以通过其他代码进行处理修复,如可以通过if判定语句来避免对年龄进行赋值时输入了字符而出现异常的情况,如使用捕捉异常可以避免除零异常等

(2)关于常见的系统异常:

除零异常(ZeroDiviceError):被除数写成了0

名称异常(NameError):变量未定义

类型异常(TypeError):不同类型数据进行相加

索引异常(IndexError):超出索引范围

键异常(KeyError):没有对应名称的键

值异常(ValueError):将字符型数据转换成整型数据

属性异常(AttributeError):对象没有对应名称的属性

迭代器异常(StopIteration):迭代次数超出迭代器内容个数

(3)关于python异常的继承关系树图

(4)异常的解决思路

系统内置了很多应用场景,我们在运行程序过程中,一旦触发相关场景,系统就会向外抛出相应的问题,这就是系统抛出的异常

预防:添加容错处理,代码虽会触发异常,但使用容错处理可以不让异常被触发

解决:如果容错代码过多时,会使得整个程序非常混乱,这时可以使用捕捉异常进行处理

解决方案1--捕捉异常的完整机构如下:

 


 

注意1:

try:
    1 / 0
    print(name)
except ZeroDivisionError as z:
    print("除零错误")
except NameError as n:
    print("名字错误")
else:
    print("代码没错")
finally:
    print("后续代码执行")

这里当1 / 0 执行时,出现异常,会跳过print(name)去执行except ZeroDivisionError as z:进行捕捉,从而不会去执行print(name),故后面对其的捕捉也是无效的

注意2:

try:
    # 1 / 0
    print(name)
except (ZeroDivisionError, NameError) as z:
    print("异常")
else:
    print("代码没错")
finally:
    print("后续代码执行")

这里如果对多个异常的处理是相同的,则可以将这些异常进行合并处理

注意3:

try:
    # 1 / 0
    print(name)
except Exception as z:
    print("异常")
else:
    print("代码没错")
finally:
    print("后续代码执行")

如果不知道具体是什么异常,可以直接使用Exception进行捕捉,因为这些常见的异常都是继承自Exception的

解决方案2--with语句

实现步骤:

1.上下文表达式context_expression要返回一个上下文管理器

2. 执行上下文管理器的__enter__()方法,如果写了as target,则把__enter__()方法的返回值赋值给target

3.执行body语句体

4.执行上下文管理器对象的__exit__()方法

注意:上下文表达式context_expression必须要返回一个上下文管理器,上下文管理器是实现了上下文管理协议的对象,而上下文管理协议中必须包含__enter__()方法和__exit__()方法。例子:

with open("a.txt", "r") as f:
    f.readlines()

这里先由open("a.txt", "r")生成上下文管理器,而该上下文管理器的__enter__()方法就是打开文件,再将文件句柄赋值给f,执行完f.readlines(),会自动执行上下文管理器的__exit__()方法,这里即关闭文件。这种写法避免了在文件处理过程中出现异常时导致的后续close()方法无法执行的问题,同时也简化了代码,避免多个文件的打开和关闭代码重复问题

(5)自定义上下文管理器

方法一:利用类来创建上下文管理器。原因--上下文管理器是一个包含__enter__()方法和__exit__()方法的对象

import traceback
class Test:
    def __enter__(self):
        print("enter")
        return "xxx"
    def __exit__(self, exc_type, exc_val, exc_tb):
        print(self, exc_type, exc_val, exc_tb)
        print(traceback.extract_tb(exc_tb))
        print("exit")
        return True
with Test() as t:
    print("测试", t)
    1 / 0

其中__enter__()方法的返回值将会赋值给t,即xxx;执行with语句体后出现的异常会抛给__exit__()方法,其参数分别代表的时异常的类型,异常的值(内容)和异常的追踪信息,异常追踪信息可以使用traceback模块的extract_tb方法单独抽取出来;__exit__()方法的返回值决定异常的抛出与否,如果返回True,则异常在内部消化,不会抛出,程序正常运行,如过返回False,则对外抛出异常,程序终止

方法二:利用生成器函数来创建上下文管理器

import contextlib
@contextlib.contextmanager
def shengchengqi():
    print(1)
    yield 6
    print(2)
with shengchengqi() as scq:
    print(3, scq)

这里需要使用contextlib模块中的contextlib.contextmanager装饰器对生成器函数进行装饰,使得其编程一个上下文管理器,这里实现的是将print(1)当作__enter__()方法执行,将print(2)当作__exit__()方法执行,而yield后面的值当作__enter__()方法的返回值,赋值给scq

使用这种方法的好处是将业务逻辑代码部分与异常处理代码部分进行分离,达到代码的解耦目的,例如:

import contextlib
@contextlib.contextmanager
def ze():
    try:
        yield
    except Exception as e:
        print("error", e)
a = 1
b = 0
with ze():
    a / b
x = 1
y = 0
with ze():
    x / y

而其原本的代码重复性比较高,并且异常处理代码和业务逻辑代码混在一起:

a = 1
b = 0
try:
    a / b
except Exception as e:
    print("error", e)
x = 1
y = 0
try:
    x / y
except Exception as e:
    print("error", e)

方法三:利用含有close()方法的类创建上下文管理器

1.一个含有close()方法的类,根据逻辑,可以这样实现上下文管理器

class A(object):
    def t(self):
        print("t")
    def close(self):
        print("释放代码")
    def __enter__(self):
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.close()
with A() as a:
    a.t()

先执行__enter__()方法,返回A类的实例对象给a,再执行语句体,使用a调用方法t(),最后自动执行__exit__()方法,在其中调用close()方法,释放代码

2.使用contextlib模块的closing()函数,可以是含有close()方法的类直接变成上下文管理器

import contextlib
class A(object):
    def t(self):
        print("t")
    def close(self):
        print("释放代码")
with contextlib.closing(A()) as a:
    a.t()

(6)多个上下文表达式的操作

python2.7之前:contextlib.nested()

import contextlib
with contextlib.nested(open("a.txt", "r") , open("a1.txt", "w")) as (from_file, to_file):
    content = from_file.read()
    to_file.write(content)

python2.7之后:嵌套或者合并

with open("a.txt", "r") as from_file, open("a1.txt", "w") as to_file:
    content = from_file.read()
    to_file.write(content)

(7)手动抛出异常--raise ExceptionType(content)

def ageSet(age):
    if age <= 0 or age > 200:
        raise ValueError("不符合")
    else:
        print("小明的年龄是%d" % age)
ageSet(18)

需要注意的是,在这个异常严重影响程序整体功能实现的时候,就必须要进行手动抛出,对异常进行修改,如果异常无伤大雅,可以直接对其进行打印,这样避免因为小的异常而终止后续程序的运行

(8)自定义异常

class Xerror(Exception):
    def __init__(self, mag, code_error):
        self.mag = mag
        self.code_error = code_error
        pass
    def __str__(self):
        return  self.mag + str(self.code_error)
def ageSet(age):
    if age <= 0 or age > 200:
        raise Xerror("不符合", 404)
    else:
        print("小明的年龄是%d" % age)
try:
    ageSet(-18)
except Exception as x:
    print("cunwu", x)

由于常见异常都是继承自Exception类的,座椅这里可以自定义一个继承了Exception的子类,在向外抛出异常时,即raise Xerror("不符合", 404) ,可以看成是创建一个Xerror实例对象,并对其初始化属性进行赋值,然后利用__str__()方法对实例对象进行描述,这就是自定义异常的方法,在最后可以对抛出的异常进行捕捉

猜你喜欢

转载自blog.csdn.net/zx870121209/article/details/81477329