Python decorators use detail

  • 2020-06-12 09:57:14
  • OfStack

A decorator is essentially an Python function that allows other functions to add additional functionality without any code changes, and the return value of the decorator is also a function object.

It is often used in scenarios with faceted requirements, such as insert logging, performance testing, transaction processing, caching, permission validation, and so on. Decorators are a great design solution for this type of problem, and with decorators, we can pull out a lot of code that is not related to the function itself and continue to be reused.

Let's start with a simple example:


def now():
  print('2017_7_29')

Now there is a new requirement to log the execution of the function, so add the logging code to the code:


def now():
  print('2017_7_29')
  logging.warn("running")

Suppose you have multiple similar requirements, what do you do? I'm going to write another logging in the now function, right? This results in a lot of code duplication. To reduce duplication, we can do this by redefining a function that specializes in logging and then executes the real business code after the logging is done.


def use_logging(func):   
  logging.warn("%s is running" % func.__name__)   
  func() 

def now():   
  print('2017_7_29') 
  
use_logging(now)

The implementation, logically, is not difficult, but in this case, we pass a function as an argument to the log function each time. Also, this approach has broken the original code logic structure, which had to be changed to use_logging(now) when the business logic was executed. So is there a better way? Of course, the answer is decorators.

The first thing to realize is that a function is also an object, and that function objects can be assigned to variables, so the function can also be called from variables. Such as:


def now():
  print('2017_7_28')

f=now
f()
#  The function object has 1 a __name__ Property, you can get the name of the function 
print('now.__name__:',now.__name__)
print('f.__name__:',f.__name__)

Simple decorator

decorator is essentially a higher-order function of a return function. Therefore, we need to define an decorator that can print logs, which can be defined as follows:


def log(func):
  def wrapper(*args,**kw):
    print('call %s():'%func.__name__)
    return func(*args,**kw)
  return wrapper

# Since log() is 1 decorator and returns 1 function, the original now() function still exists,
# is just that the now variable with the same name now points to the new function, so calling now() will execute the new function, the wrapper() function returned in log().
The argument definition of the # wrapper() function is (*args, **kw), so the wrapper() function can accept any argument.
Within the wrapper() function, first print the log, then call the original function.

log above, since it is 1 decorator, takes 1 function as an argument and returns 1 function. Now execute:


now = log(now)
now()

Output results:
call now():
2017_7_28

The function log is the decorator, which wraps the func that performs the real business method in a function that looks like now is decorated with log. In this example, when the function comes in, it is referred to as 1 cross-section (Aspect), and this programming approach is referred to as aspect-oriented programming (ES66en-ES67en Programming).

Use grammar sugar:


@log
def now():
  print('2017_7_28')

The @ symbol is the syntax sugar for the decorator and is used when defining functions to avoid one more assignment

In this way, we can omit the sentence now = log(now) and call now() directly to get the desired result. If we have other similar functions, we can continue to call the decorator to modify the function without having to repeatedly modify the function or add a new wrapper. In this way, we improve the reusability and readability of the program.

The ease with which decorators are used in Python is due to the fact that Python's functions can be passed as arguments to other functions, can be assigned to other variables, can be returned as values, and can be defined in another function, just like ordinary object 1.

Decorator with parameters:

If decorator itself needs to pass in parameters, then you need to write a higher-order function that returns decorator, which is a bit more complicated to write. For example, to customize the text of log:


def log(text):
  def decorator(func):
    def wrapper(*args,**kw):
      print('%s %s()'%(text,func.__name__))
      return func(*args,**kw)
    return wrapper
  return decorator

This 3-layer nested decorator is used as follows:


@log('goal')
def now():
  print('2017-7-28')
now()

Is equivalent to

now = log('goal')(now)

# First execute log('execute'), return the decorator function, then call the returned function with the argument now function, return the value wrapper function
now()

Because we've shown that the functions are also objects, with attributes such as ___, but if you look at the ___ with decorator, their ___ has changed from 'now' to 'wrapper' :


print(now.__name__)
# wrapper

Because the returned wrapper() function name is 'wrapper', you need to copy the attributes such as the original function with each ___ 123EN__ to the wrapper() function, otherwise some code execution that depends on the function signature will fail.

Per 127en. ___, 128en__ = func. ___, functools. wraps is built into Python.


def now():
  print('2017_7_29')
  logging.warn("running")
0

def now():
  print('2017_7_29')
  logging.warn("running")
1

Class decorator:

Look again class decorator, compared with function decorator, class decorator has flexibility, high cohesion, packaging and other advantages. Using the class decorator can also rely on the method within the class with succcall__, which is called when the decorator is attached to the function using the @ form


def now():
  print('2017_7_29')
  logging.warn("running")
2

Conclusion:

In a nutshell, the purpose of a decorator is to add additional functionality to an already existing object.

In the object-oriented (OOP) design pattern, decorator is called decorative pattern. OOP's decorative pattern needs to be implemented through inheritance and composition, and Python supports decorator directly from the syntax level in addition to decorator, which supports OOP. Python's decorator can be implemented using either functions or classes.


Related articles: