In Python, decorators are a very powerful and useful syntax that makes repetitive code easier to understand and read.

So here’s a quick summary of how Python decorators can be used and what to look out for.

Get the parameters of the decorated function in the decorator

Suppose we were developing the Web and needed to do the reverse crawl. To determine the source of the interface’s access, we can use the following decorator methods:

def mydecorator(func):
    def wrapped(*args, **kwargs):
        print("Enter the decorator")
        if args[0] ['header'] = ='spider':
            print("code: 400")
            return

        result = func(*args, **kwargs)
        return result

    return wrapped
@mydecorator
def request_page(request):
    print("An access request")
    print("Response is returned.")
if __name__ == '__main__':
    request = {
        'data': 100.'header': 'spider'
    }
    request_page(request)
Copy the code

In this decorator, we get the header argument from the request in the decorator, and if we judge that the access came from the crawler, we return it a 400.

Writing with decorators is equivalent to writing without decorators below

def mydecorator(*args, **kwargs):
    print("Enter the function")
    if args[0] ['header'] = ='spider':
        print("code: 400")
        return False
    return True
def request_page(request):
    if not mydecorator(request):
        return
    print("Visit a web page")
    print("Got a response")
if __name__ == '__main__':
    request = {
        'data': 100.'header': 'spider'
    }
    request_page(request)
Copy the code

The latter method may be better than the decorator method when only one function needs to be decorated, but when many functions need to be decorated, the decorator is clearly the better choice.

Get the return value of the function in the decorator

Decorators can also be used when we need to make a judgment about the return value of a function, but do not want to write the judgment directly in the function:

def mydecorator(func):
    def wrapped(*args, **kwargs):
        print("Enter the decorator")
        result = func(*args, **kwargs)
        if result == 400:
            print("response is 400!")
            return False
        return True
    return wrapped

@mydecorator
def request_page(a):
    print("Visit a web page")
    print("Got a response")
    return 200

if __name__ == '__main__':
    print(request_page())
Copy the code

Pass parameters to the decorator

In practice, we sometimes need to repeat the execution of a function according to its execution state. For example, when compiling crawler, some pages may fail to visit due to network reasons, so we need to make repeated requests according to the return results of crawler.

def retry(MAXRETRY=3):
    def decorator(func):
        def wrapped(*args, **kwargs):
            print("Enter the decorator")

            result = 0
            retry = 1
            whileresult ! =200 and retry <= MAXRETRY:
                result = func(*args, **kwargs)
                print("Try again %s" % retry)
                retry += 1

            return result

        return wrapped

    return decorator
    
@retry(5)
def request_page(a):
    print("Visit a web page")
    print("Got a response")
    return 400

Copy the code

In this case, let’s assume that if you visit a web page and get 400, you re-request it. We passed a 5 in the Retry decorator, which means that we want the maximum number of retries to be 5, or its default is 3 if this value is not passed.

After being familiar with the writing method of the basic decorator, the writing method of the passing reference decorator is also very good to understand. It’s just an extra layer of functions for passing in parameters.

4. Problems with decorator documents

We all know that the magic __doc__ method can get the document we wrote in code, but did you know that using a decorator can cause the document wrapped by the function to be overwritten by the decorator’s document?

def request_page(a):
    "Request_page function document :return:"
    print("Visit a web page")
    print("Got a response")

if __name__ == '__main__':
    print(request_page.__doc__)
Copy the code

When we use the __doc__ method on the undecorated code above, we get:

In[3]: request_page.__doc__ Out[3]: '\n request_Page function document \n :return:\n 'Copy the code

This is what we want!

But when we decorate the above functions with decorators:

def decorator(func):
    def wrapped(*args, **kwargs):
        "Decorator document: Param args: : Param kwargs: :return:"
        print("Enter the decorator")
        result = func(*args, **kwargs)
        return result

    return wrapped


@decorator
def request_page(a):
    "Request_page function document :return:"
    print("Visit a web page")
    print("Got a response")
    
Copy the code

When we run the __doc__ magic method again, the result is the decorator’s internal document:

In[4]: request_page.__doc__
Out[4] :'\n Decorator Document \n: Param args:\n: Param kwargs:\n :return:\n '
In[5]: request_page.__name__
Out[5] :'wrapped'
Copy the code

This problem can make debugging difficult, and it can render many automatic document generation tools ineffective.

The best way to solve this problem is to wrap the decorator in wraps using the FuncTools package’s wraps() module.


from functools import wraps
def decorator(func):

    @wraps(func)
    def wrapped(*args, **kwargs):
        Decorator: Param args: : Param kwargs: :return:"
        print("Enter the decorator")
        result = func(*args, **kwargs)
        return result
    return wrapped

@decorator
def request_page(a):
    "Request_page function document :return:"
    print("Visit a web page")
    print("Got a response")
    
Copy the code

Wrap the wraps so that our function can save some of its important data.

In[3]: request_page.__doc__
Out[3] :'\n request_page function document \n :return:\n '
In[3]: request_page.__name__
Out[4] :'request_page'
Copy the code

5. Write decorators using class notation

While most decorators are written as functions, it is equally possible to write decorators as classes.

Using class writing, we can implement requirements that are difficult to implement using function writing. For example, the number of times a function is executed

class Decorator(a):
    def __init__(self,func):
        print('Class initialization')
        self.func = func
        self.count = 0
    def __call__(self, *args, **kwargs):
        print('Enter decorator')
        result = self.func(*args,**kwargs)
        self.count += 1

        return result
@Decorator
def request_page(a):
    ''' request_page :return: '''
    print("Visit a web page")
    print("Got a response")
Copy the code

Six, summarized

Decorators are a relatively advanced syntax in Python, and here are just a few tips on how to use them and what to look out for. To borrow the words of Jin Yong, “martial arts are not high or low, but deep or shallow.” Want to more flexible use of decorators, in-depth understanding of its principle, we usually need to strengthen the study of basic skills!