Detailed explanation of Python decorator code

  • 2021-12-11 08:16:53
  • OfStack

Catalog 1. Understanding Decorators 2. Decorator Prototypes 1, Decorators without Parameters 2. Decorated Functions with Parameters 3. Decorators with Parameters 4. Using Classes as Decorators 5. Using Objects as Decorators 6. Nested Summary of Multilayer Decorators

STEP 1 Understand decorators

Everything is an object (functions can be passed as objects)
Since a function is also an object, and the function object can be assigned to a variable, the function can also be called through a variable.


def function_one():
    print(" Test function ")
# You can set the 1 A function is assigned to 1 Variables, such as 
foo =function_one # Parentheses are not used here, because we are not calling function_one Function , It's putting it in foo In the variables. 
foo()
'''
 Test function 
Process finished with exit code 0
'''

The concept of closure:

1) Function nesting

2) Internal functions use variables of external functions

3) The return value of the outer function is the inner function

Example:


def outer_function(message):
    def inner_function():
        print(message)
    return inner_function
func = outer_function(" How do you do ")
func() # How do you do 

2. Prototype of decorator

The function of the decorator is to add new functions to the original function without modifying the source code and the way the original function is called.


# Pass the function as an argument to another 1 Functions 
def decorator_function(original_function):
    def wrapper_function():
    	print('wrapper executed this before {}'.format(original_function.__name__))
        original_function()
    return wrapper_function
    '''
     Return wrapper_function Instead of wrapper_function() ; This is because when you put 1 Put the parentheses behind, and this function will be executed; 
     However, if you don't put parentheses after it, it can be passed around and can be assigned to another variable without executing it. 
	'''
def display():
    print('display function ran')
decorator_display = decorator_function(display)
decorator_display()

Run results:


wrapper executed this before display
display function ran
Process finished with exit code 0

1. Decorator without parameters


def decorator_function(original_function):
    def wrapper_function():
        print('wrapper executed this before {}'.format(original_function.__name__))
        original_function()
    return wrapper_function
@decorator_function
def display():  # Equivalent to display =decorator_function(display)
    print('display function ran')
display()

Run results:

wrapper executed this before display
display function ran

Process finished with exit code 0

2. Decorated functions with parameters


def decorator_function(original_function):
    def wrapper_function(*args,**kwargs):
        print('wrapper executed this before {}'.format(original_function.__name__))
        original_function(*args,**kwargs)
    return wrapper_function
@decorator_function
def display():
    print('display function ran')
@decorator_function
def display_info(name,age):
    print('display_info ran with arguments ({},{})'.format(name,age))
display()
print('='*50)
display_info('Michal',20)

Run results:

wrapper executed this before display
display function ran
==================================================
wrapper executed this before display_info
display_info ran with arguments (Michal,20)

Process finished with exit code 0

Running the following code will cause 1 problem


def decorator_function(original_function):
    def wrapper_function(*args,**kwargs):
        print('wrapper executed this before {}'.format(original_function.__name__))
        original_function(*args,**kwargs)
    return wrapper_function
@decorator_function
def display():
    print('display function ran')
@decorator_function
def display_info(name,age):
    print('display_info ran with arguments ({},{})'.format(name,age))
display_info = decorator_function(display_info)
print(display_info.__name__)

wrapper_function

Process finished with exit code 0

The output should be display_info, where the function is replaced by wrapper_function, rewriting the name of our function and the comment document (docstring). functools. wraps can be used in Python to solve this problem.


from functools import wraps
def decorator_function(original_function):
    @wraps(original_function)
    def wrapper_function(*args,**kwargs):
        print('wrapper executed this before {}'.format(original_function.__name__))
        original_function(*args,**kwargs)
    return wrapper_function
@decorator_function
def display():
    print('display function ran')
@decorator_function
def display_info(name,age):
    print('display_info ran with arguments ({},{})'.format(name,age))
display_info = decorator_function(display_info)
print(display_info.__name__)

Run results:

display_info

Process finished with exit code 0

3. Decorator with parameters

Embedding decorators in functions


from functools import wraps
def logit(logfile='out.log'):
    def logging_decorator(func):
        @wraps(func)
        def wrapped_function(*args, **kwargs):
            log_string = func.__name__ + " was called"
            print(log_string)
            #  Open logfile And write the content 
            with open(logfile, 'a') as opened_file:
                #  Now type the log to the specified logfile
                opened_file.write(log_string + '\n')
            return func(*args, **kwargs)
        return wrapped_function
    return logging_decorator
@logit()
def myfunc1():
    pass
myfunc1()
# Output: myfunc1 was called
#  Now 1 A man called  out.log  The file of appears, and the contents inside are the above string 
@logit(logfile='func2.log')
def myfunc2():
    pass
myfunc2()
# Output: myfunc2 was called
#  Now 1 A man called  func2.log  The file of appears, and the contents inside are the above string 

4. Use classes as decorators


class myDecorator(object):
     def __init__(self, f):
         print("inside myDecorator.__init__()")
         f() # Prove that function definition has completed
     def __call__(self):
         print("inside myDecorator.__call__()")
 ​
 @myDecorator
 def aFunction():
     print("inside aFunction()")
 ​
 print("Finished decorating aFunction()")
 ​
 aFunction()

Run results:


def outer_function(message):
    def inner_function():
        print(message)
    return inner_function
func = outer_function(" How do you do ")
func() # How do you do 
0

The decorated function aFunction () is actually an object of class myDecorator. When you call the aFunction () function again, you are actually calling the object of class myDecorator, so the __call__ () method of class myDecorator is called.

So when you use a class as a decorator to decorate a function to add 1 extra attribute or functionality to a function, 1 typically records the incoming function in the __init__ () method of the class, and calls the decorated function and other extra processing in __call__ ().


def outer_function(message):
    def inner_function():
        print(message)
    return inner_function
func = outer_function(" How do you do ")
func() # How do you do 
1

Run results:

Entering func1
inside func1()
Exited func1
Entering func2
inside func2()
Exited func2

Process finished with exit code 0

5. Use objects as decorators

Space parameter:


def outer_function(message):
    def inner_function():
        print(message)
    return inner_function
func = outer_function(" How do you do ")
func() # How do you do 
2

The running results are as follows:

Execute the __init__ () method of the decorator_class class
Execute the __call__ () method of the decorator_class class
call method executed this before display_info
Execute display_info ()
display_info ran with arguments (Michael,20)
display_info () finished executing

Process finished with exit code 0

With parameters:


from functools import wraps
class decorator_class:
    def __init__(self,arg1, arg2):
        print(' Execute decorator_class Class __init__() Method ')
        self.arg1 =arg1
        self.arg2=arg2
    def __call__(self, original_function):
        print(' Execute decorator_class Class __call__() Method ')
        @wraps(original_function)
        def wrapped_function(*args, **kwargs):
        	print(' Execute wrapped_function()')
            print('call method executed this before {}'.format(original_function.__name__))
            print(' Decorator parameters: ', self.arg1, self.arg2)
            print(' Execute ' + original_function.__name__ + '()')
            original_function(*args, **kwargs)
            print(original_function.__name__ + '() Complete execution ')
        return wrapped_function
@decorator_class('Hello', 'World')
def display_info(name,age):
    print('display_info ran with arguments ({},{})'.format(name,age))
display_info('Michael',20)

The running results are as follows:

Execute the __init__ () method of the decorator_class class
Execute the __call__ () method of the decorator_class class
Execute wrapped_function ()
call method executed this before display_info
Decorator Parameters: Hello World
Execute display_info ()
display_info ran with arguments (Michael,20)
display_info () completed execution

Process finished with exit code 0

Example 2:


def outer_function(message):
    def inner_function():
        print(message)
    return inner_function
func = outer_function(" How do you do ")
func() # How do you do 
4

6. Nesting of multi-layer decorators


def outer_function(message):
    def inner_function():
        print(message)
    return inner_function
func = outer_function(" How do you do ")
func() # How do you do 
5

Run results:

2----Before Decoration 2
1----before decorating 1
Test
1----After Decoration 1
2----After Decoration 2

Process finished with exit code 0

Summarize

This article is here, I hope to give you help, but also hope that you can pay more attention to this site more content!


Related articles: