Use decorator to decorate the class method and call the class method in the decorator.

Posted by bschwarz on Tue, 23 Jun 2020 12:36:23 +0200

Decorator methods outside the class decorate class methods and call other methods of the class

Scenario: a class that links to a database often disconnects when performing database operations. At this time, you need to call the class's connect database method to re link to the database

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

    def connect(self):
        print("Database link succeeded")


    def insert(self):
        # Database disconnection exception may occur
        print("Saving operation in progress")
        for i in range(self.try_count):
            try:
                print("Save data")
                # raise NameError
                return
            except Exception:
                time.sleep(1)
                self.connect()
                print(traceback.format_exc())

    def select(self):
        # Database disconnection exception may occur
        print("Query data")

    def delete(self):
        # Database disconnection exception may occur
        print("Delete data")

As in the insert method, you can catch exceptions, relink them, and then operate.

However, no matter how to add, delete, modify or search, it needs to be written in this way. The code is redundant. It's natural to use decorators.

Example:

The conventional way of writing decorators

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("link error---{}---second".format(i))
                print("Call the database link method, and link the database again")
        print("Information saving failed")

    return wrapper


@catch_error
def insert():
    print("Start insert statement")
    raise TimeoutError

Now we want to decorate the class method and call the database link method to re link after catching the database link exception.

Solution: add the self parameter to the wrapper parameter of the decorator's internal function, as follows, you can use self to call the connect method.

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("link error---{}---second".format(i))
                print("Call the database link method, and link the database again")

                self.connect()

        print("Information saving failed")

    return wrapper

Full code:

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("link error---{}---second".format(i))
                print("Call the database link method, and link the database again")

                self.connect()

        print("Information saving failed")

    return wrapper


@catch_error
def insert():
    print("Start insert statement")
    raise TimeoutError


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

    def connect(self):
        print("Database link succeeded")

    @catch_error
    def insert(self):
        # Database disconnection exception may occur
        print("Save data")
        raise TimeoutError

    def select(self):
        # Database disconnection exception may occur
        print("Query data")

    def delete(self):
        # Database disconnection exception may occur
        print("Delete data")


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

But there is a hole in it:

  • In this decorator, to catch an Exception, you must use Exception to catch it! It cannot be any other Exception, even if the Exception thrown and the Exception to be caught are the same Exception.
  • Decorating ordinary functions has no effect

As follows:

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("link error---{}---second".format(i))
                print("Call the database link method, and link the database again")

                self.connect()

        print("Information saving failed")

    return wrapper
    
@catch_error
def insert(self):   # Class method
    # Database disconnection exception may occur
    print("Save data")
    raise NameError
    
    
//Database link succeeded
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'

This article is based on the official title of Qing Nan
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

Topics: Python Database Windows