优雅地处理python异常

异常基础

在python代码中捕获异常,可以使用try/except语句。它的基本形式如下:

try:    
# 需要检查的代码
except Exception as e:    
# 处理异常的代码

还可以使用finally子句,在异常发生时执行一些清理工作,以及不管是否发生异常都要执行的操作。

try:
# 需要检查的代码
except Exception as e:
# 处理异常的代码
finally:
# 不管是否发生异常都要执行的代码

此外,在except子句中,可以根据不同的异常类型使用不同的处理方式,以便更加精确地处理异常。

try:
    # 需要检查的代码
except ValueError as e:
    # 处理ValueError异常的代码
except TypeError as e:
    # 处理TypeError异常的代码
except Exception as e:
    # 处理其他异常的代码

可以发现,为了给一个方法添加异常处理,需要在方法中添加大量的try/except语句,这样会使代码变得很冗长,不易阅读。因此,笔者尝试一种更加优雅的方式来处理异常。

异常处理装饰器

笔者的初步构思是我只需要给需要捕捉异常的函数添加一个装饰器,随后我们可以将该函数的各类异常分离出来,统一处理。这样就可以避免在函数中添加大量的try/except语句。

# 伪代码

@tryme
def func():
    # 需要检查的代码
    print(1 / 0)

@func.exception(ZeroDivisionError)
def handle_zero_division_error(e):
    # 处理ZeroDivisionError异常的代码
    print(e)

这样,当func函数发生ZeroDivisionError异常时,就会调用handle_zero_division_error函数来处理异常。

观察以上伪代码,首先我们在func函数上添加了一个装饰器@tryme,这点不难理解,而后面我们添加异常装饰器是使用@func.exception,但是我们的func函数并没有exception属性,这是怎么回事呢?其实这也不难,我们只需要在@tryme的装饰器中,将func函数的exception属性指向一个新的函数,这个函数的作用就是添加异常处理函数。

代码实现

from functools import wraps
from typing import Callable, Dict, Any

def tryme(func):
    exception_: Dict[Any, Callable] = {}

    @wraps(func)
    def wrapper(*args, **kwargs):
        try:
            ret = func(*args, **kwargs)
            if ret is not None:
                return ret
        except Exception as e:
            handler = None
            for c in exception_.keys():
                if isinstance(e, c):
                    handler = c

            if handler is None:
                raise e

            return exception_[handler](e)

    def except_(*exceptions):
        def decorator(f):
            for e in exceptions:
                exception_[e] = f
            return f

        return decorator
    # 将exception属性指向except_函数
    # 这样就可以使用@func.exception来添加异常处理函数
    wrapper.exception = except_
    return wrapper

@tryme
def my_function():
    print(1 / 0)

@my_function.exception(ZeroDivisionError)
def handle_zero_division_error(e):
    print('zero division error')


if __name__ == '__main__':
    my_function()

这一版本中有个不太合理的地方,假设我有10个函数需要捕捉某个指定异常,岂不是要写10次@my_function.exception(ZeroDivisionError)?这样的代码显然不够优雅,因此我们需要改进一下。使用类来封装异常装饰器,同一实例化的对象可以共享异常处理函数。

from functools import wraps
from typing import Callable, Dict, Any

class TryMe:

    def __init__(self):
        self.exception_: Dict[Any, Callable] = {}

    def try_(self, func):

        @wraps(func)
        def wrapper(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except Exception as e:
                handler = None
                for c in self.exception_.keys():
                    if isinstance(e, c):
                        handler = c

                if handler is None:
                    raise e
                # 将异常发生的函数和异常对象传入异常处理函数
                return self.exception_[handler](func, e)

        return wrapper

    def except_(self, *exceptions):
        def decorator(f):
            for e in exceptions:
                self.exception_[e] = f
            return f

        return decorator


tryme = TryMe()

@tryme.try_
def my_function():
    print(1 / 0)
    print('hello world')

@tryme.try_
def my_function2():
    print(1 / 0)
    print('hello world')

@tryme.except_(ZeroDivisionError)
def handle_zero_division_error(func, e):
    print(func.__name__, str(e))

if __name__ == '__main__':
    my_function()
    my_function2()

输出:
my_function division by zero
my_function2 division by zero

这样,我们可以统一封装异常函数,由于调用异常函数时,会传入异常发生的函数和异常对象,因此我们可以在异常函数中获取异常发生的函数的信息,比如函数名、参数等。

总结

本文不仅介绍了Python中的异常处理机制,还实现了一个简单的异常装饰器。面对多个异常需要在函数后追加各种except语句,显得代码不够优雅,因此我们可以使用装饰器来实现异常处理,这样可以使代码更加简洁。

为此,笔者开发了一个trytry库:

---------------------------END---------------------------

题外话

在这里插入图片描述

感兴趣的小伙伴,赠送全套Python学习资料,包含面试题、简历资料等具体看下方。

👉CSDN大礼包🎁:全网最全《Python学习资料》免费赠送🆓!(安全链接,放心点击)

一、Python所有方向的学习路线

Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照下面的知识点去找对应的学习资源,保证自己学得较为全面。

img
img

二、Python必备开发工具

工具都帮大家整理好了,安装就可直接上手!img

三、最新Python学习笔记

当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。

img

四、Python视频合集

观看全面零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。

img

五、实战案例

纸上得来终觉浅,要学会跟着视频一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。

img

六、面试宝典

在这里插入图片描述

👉CSDN大礼包🎁:全网最全《Python学习资料》免费赠送🆓!(安全链接,放心点击)

若有侵权,请联系删除

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值