类外的装饰器方法装饰类方法,并调用该类的其他方法
场景:链接数据库的类,在执行数据库方面的操作时,经常会断开,此时需要调用该类的连接数据库方法来重新链接到数据库
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_errordef 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 timeimport tracebackdef 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_errordef insert(): print("开始执行插入语句") raise TimeoutErrorclass 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_errordef 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