1. Decorator mode

Decorator pattern is one of the common software design patterns. With this design pattern, we can assign new responsibilities to existing objects without changing any of the underlying code. Decorator patterns can be easily implemented in Python with decorators.

Qun: 850973621, There are free video tutorials, development tools,

Electronic books, project source code sharing. Exchange and learn together, make progress together!

1.1 Passing functions as arguments

In C/C++, a function pointer can pass a function as an argument to another function. In Python, functions are also objects. Functions can be referred to, passed directly as arguments to functions, or as elements of container objects. Decorator pattern can be implemented in Python as follows:

#! /usr/bin/env python3.6 # -* -coding: utf-8 -* -def add(x, y): result = x+y return result def log(func): /usr/bin/env python3.6 # -* -coding: utf-8 -* -def add(x, y): result = x+y return result def log(func): def wrapper(*args, **kwargs): result = func(*args) print(func.__name__,'has been called\n') return result return wrapper if __name__ == '__main__': Print (log (add) (1, 2))Copy the code

In the above code, the log function takes the function to be decorated and returns the function object. The parameters of the returned function are variable parameters *args and **kwargs (*args is encapsulated as a tuple, and **kwargs is encapsulated as a dictionary object), so as to adapt to the different parameters of different functions and ensure universality.

1.2 a decorator

The above implementation method is a bit cumbersome, and all the code that calls the decorated function needs to be modified accordingly, which is not consistent with Python’s simplicity and legibility. Hence the syntax sugar given in Python to increase readability and ease of use is called a decorator.

#! /usr/bin/env python3.6 # -* -coding: utF-8 -*- from functools import wraps log(func): #@wraps(func) def wrapper(*args, **kwargs): Result = func(*args) print(func.__name__,'has been called') return result return wrapper # equivalent to add = log(add) @log def Add (x, y) : the result = x + y return result if __name__ = = "__main__ ': print (add (1, 2)) print (add. __name__)Copy the code

The operation is as follows:

>>print(add(1,2))
add has been called
3
>>print(add.__name__)
wrapper

Copy the code

However, there are drawbacks to this approach, as metadata (such as names, docstrings, annotations, and parameter signatures) of the original add function can be lost. To avoid bugs, whenever you define a decorator, use the @Wraps decorator from the FuncTools library to annotate the underlying wrapping function (the comment part of the code). An important feature of @wraps is that it gives you direct access to the wrapped function via the __wrapped__ attribute. Operation condition after improvement:

>>print(add(1,2))
add has been called
3
>>print(add.__name__)
add

Copy the code

1.3 Remove the decorator

When a decorator has been applied to a function and you want to undo it, access the __wrapped__ attribute to access the original function

Orig_add = add. __wrapped__ orig_add (1, 2)Copy the code

However, if you use multiple decorators, the __wrapped__ attribute becomes uncontrollable and should be avoided as much as possible. If the following code exists:

#! /usr/bin/env python3.6 # -* -coding: utF-8 -* -import functools import time def metric(func): @functools.wraps(func) def wrapper(*args,**kv): print('Decorator1') f = func(*args,**kv) return f return wrapper def logging(func): @functools.wraps(func) def wrapper(*args,**kv): print('Decorator2') f = func(*args,**kv) return f return wrapper @metric @logging def normalize(name): sName = name[0:1].upper() + name[1:].lower() print(sName) if __name__ == '__main__': normalize('heLlO') normalize.__wrapper__('')Copy the code

The operation is as follows:

>>normalize('helLo')
Decorator1
Decorator2
Hello
>>normalize.__wrapped__('world')
Decorator2
World

Copy the code

1.4 Define decorators with parameters

from functools import wraps

def log(text):
    def decorator(func):
        @wraps(func) 
        def wrappering(*args,**kv):
            print('%s %s():'%(text,func.__name__))
            return func(*args,**kv)
        return wrappering
    return decorator

@log('run')
def normalize(name):
    sName = name[0:1].upper() + name[1:].lower()
    print(sName)

Copy the code

Decorator functions may take arguments, and the outermost functions pass arguments to the inner decorator functions, known as wrappering functions, which use log passed arguments. Decorator processing is equivalent to the following:

normalize = log('run')(normalize)

Copy the code