乐趣区

使用装饰器装饰类方法并在装饰器中调用类方法

类外的装饰器方法装饰类方法,并调用该类的其他方法

场景:链接数据库的类,在执行数据库方面的操作时,经常会断开,此时需要调用该类的连接数据库方法来重新链接到数据库

class My_dbclient():
    def __init__(self):
        self.try_count=10
        self.connect()

    def connect(self):
        print("数据库链接成功")


    def insert(self):
        # 可能会出现数据库断开链接的异常
        print("正在执行保存操作")
        for i in range(self.try_count):
            try:
                print("保存数据")
                # raise NameError
                return
            except Exception:
                time.sleep(1)
                self.connect()
                print(traceback.format_exc())

    def select(self):
        # 可能会出现数据库断开链接的异常
        print("查询数据")

    def delete(self):
        # 可能会出现数据库断开链接的异常
        print("删除数据")

就像 insert 方法中这样,可以捕获异常,并重新链接,然后再操作。

但是,无论增删改查,都需要这样来写,代码冗余。很自然,使用装饰器来解决。

示例:

常规的装饰器写法

def catch_error(fun):
    def wrapper(*args, **kwargs):
        for i in range(1, 10):
            try:
                fun(*args, **kwargs)
                return
            except Exception:
                time.sleep(1)
                print("链接错误 ---{}--- 次".format(i))
                print("调用数据库链接方法,重新链接数据库")
        print("信息保存失败")

    return wrapper


@catch_error
def insert():
    print("开始执行插入语句")
    raise TimeoutError

现在我们想要装饰类方法,并在捕获数据库链接异常后,调用数据库链接方法重新链接。

解决办法:在装饰器的内部函数 wrapper 的参数中,增加 self 这个参数, 如下,就可以使用 self 来调用 connect 这个方法了。

def catch_error(fun):
    def wrapper(self,*args, **kwargs):
        for i in range(1, 10):
            try:
                fun(*args, **kwargs)
                return
            except Exception:
                time.sleep(1)
                print("链接错误 ---{}--- 次".format(i))
                print("调用数据库链接方法,重新链接数据库")

                self.connect()

        print("信息保存失败")

    return wrapper

完整代码:

import time
import traceback


def catch_error(fun):
    def wrapper(self,*args, **kwargs):
        for i in range(1, 10):
            try:
                fun(*args, **kwargs)
                return
            except Exception:
                time.sleep(1)
                print("链接错误 ---{}--- 次".format(i))
                print("调用数据库链接方法,重新链接数据库")

                self.connect()

        print("信息保存失败")

    return wrapper


@catch_error
def insert():
    print("开始执行插入语句")
    raise TimeoutError


class My_dbclient():
    def __init__(self):
        self.try_count = 10
        self.connect()

    def connect(self):
        print("数据库链接成功")

    @catch_error
    def insert(self):
        # 可能会出现数据库断开链接的异常
        print("保存数据")
        raise TimeoutError

    def select(self):
        # 可能会出现数据库断开链接的异常
        print("查询数据")

    def delete(self):
        # 可能会出现数据库断开链接的异常
        print("删除数据")


if __name__ == '__main__':
    t = My_dbclient()
    t.insert()

但是,这里面有个坑:

  • 在这个装饰器中,捕获异常,必须使用 Exception 来捕获!而不能是其他的异常,即使引发的异常和要捕获的异常是同一个异常也不行。
  • 装饰普通函数则没有影响

如下:

def catch_error(fun):
    def wrapper(self,*args, **kwargs):
        for i in range(1, 10):
            try:
                fun(*args, **kwargs)
                return
            except NameError:
                time.sleep(1)
                print("链接错误 ---{}--- 次".format(i))
                print("调用数据库链接方法,重新链接数据库")

                self.connect()

        print("信息保存失败")

    return wrapper
    
@catch_error
def insert(self):   # 类方法
    # 可能会出现数据库断开链接的异常
    print("保存数据")
    raise NameError
    
    
数据库链接成功
Traceback (most recent call last):
  File "D:/test.py", line 54, in <module>
    t.insert()
  File "D:/test.py", line 9, in wrapper
    fun(*args, **kwargs)
TypeError: insert() missing 1 required positional argument: 'self'

这篇文章借鉴了靑南大佬的公号文章:
https://mp.weixin.qq.com/s?__biz=MzI2MzEwNTY3OQ==&mid=2648978488&idx=1&sn=4fc271eebbb88273cb7f280b3b1389f5&chksm=f25069d8c527e0cecc3e62f431a0c3a92d425345f18f37b7f7bbf2b12e0578d725197480583a&mpshare=1&scene=1&srcid=&sharer_sharetime=1592892345783&sharer_shareid=463653a5a10c5bdfdaa178eb8a3a2da0&key=32686516f8c11656ca6208b19edd6c0d7b1e05bbe9fb0be63d3b0d9a00dab386c78b3177eba6325998d7f52dc70242b1eae81a0550cb1b5d5d5ac8a9cebf29d920dc5436e71606e81ea34c9ed54ebb91&ascene=1&uin=MjE2MTA1ODMxMg%3D%3D&devicetype=Windows+10&version=62070158&lang=zh_CN&exportkey=AWW29fqNMgGY6iLMschplCM%3D&pass_ticket=fpX5pWKrl3sa09YyD4bLp3t7V9wWI6%2BY7Bf1Umfke7FK8Xgia4WMG3wnNRiAmh4D

退出移动版