Python @decorator (function + class decorator)


Decorators are functions that modify the functionality of other functions

function

You can pass a function to a variable in Python

def func():
    print("say hello")

a = func
print(a) # <function func at 0x0000025F271C6DC0>
a()  # say hello
func() # say hello

Delete the original function and see what happens

del func
print(a) # <function func at 0x0000025F271C6DC0>
print(func) # name 'func' is not defined

We can also define a function inside the function and assign it to other variables (the function name is also used as a variable)

def func():

    def say():
        print("say hello")
    return say

def not_say():
    print("say goodbye")

not_say = func()
print(not_say) # <function func.<locals>.say at 0x0000025F271C68B0>
not_say() # say hello

sayAs you can see, we assign the function to the function through the function callnot_say

first decorator

def func_decorator(a_func):
 
    def wrapTheFunction():
        print("I am doing some boring work before executing a_func()")
 
        a_func()
 
        print("I am doing some boring work after executing a_func()")
 
    return wrapTheFunction
 
def my_func():
    print("I am the function which needs some decoration to remove my foul smell")
 
my_func()
#outputs: "I am the function which needs some decoration to remove my foul smell"
 
my_func = func_decorator(my_func)
#now a_function_requiring_decoration is wrapped by wrapTheFunction()
 
my_func()
#outputs:I am doing some boring work before executing a_func()
#        I am the function which needs some decoration to remove my foul smell
#        I am doing some boring work after executing a_func()

This is what Python decorators do, they encapsulate a function and modify its behavior in one way or another, so @how do you express it with the symbol?

@func_decorator
def my_func():
    """Hey you! Decorate me!"""
    print("I am the function which needs some decoration to "
          "remove my foul smell")
 
my_func()

#the @func_decorator is just a short way of saying:
# my_func = func_decorator(my_func)

@func_decoratorIn fact my_func = func_decorator(my_func), the function is to change the function of the current function (here is my_func)

But there is a problem here. Doing so is equivalent to my_funcoverwriting the original or directly intowrapTheFunction

print(my_func.__name__) # wrapTheFunction

This is not what we want, we just want to change the function of the original function, but still want to be able to access other properties of the original function

  • Python provides functionsfunctools.wraps
    • @wrapsAccepts a function to decorate, and adds the ability to copy the function name, comment documentation, parameter list, etc. This allows us to access the properties of the function before the decoration inside the decorator
from functools import wraps
def func_decorator(a_func):
    @wraps(a_func)
    def wrapTheFunction():
        print("I am doing some boring work before executing a_func()")
        a_func()
        print("I am doing some boring work after executing a_func()")
        
    return wrapTheFunction

@func_decorator
def my_func():
    """Hey you! Decorate me!"""
    print("I am the function which needs some decoration to "
          "remove my foul smell")
 
print(my_func.__name__) # my_func

application

Logging

from functools import wraps
 
def logit(func):
    @wraps(func)
    def with_logging(*args, **kwargs):
        print(func.__name__ + " was called")
        return func(*args, **kwargs)
    return with_logging
 
@logit
def addition_func(x):
   """Do some math."""
   return x + x
 
 
result = addition_func(4)
# Output: addition_func was called

class as decorator

To use a class as a decorator, a method needs to be defined inside the class __call__.

  • __call__Is a magic method that can call an instance of this class like a function
class Person:
    def __init__(self) -> None:
        self.num = 10
        self.name = "nsy"
    
    def __call__(self):
        print(self.num)
    
a = Person()
a() # 10, 这里就是调用了 __call__ 方法

We use classes as decorators

class Person:
    def __init__(self, func) -> None:
        self.num = 10
        self.name = "nsy"
        self.func = func
    
    def __call__(self, *args):
        print("Process before my func")
        self.func(*args)
        print("Process after my func")
        
@Person
def say_name(name): # say_name = Person(say_name), 实例化了一个对象
    print(f"my name is {
      
      name}")

say_name("nsy") # 调用 __call__ 方法
  • This is actually equivalent here say_name = Person(say_name), which creates an Personobject instance, and then we say_name("nsy")call __call__the method when calling , so that the class is used as a decorator

Reference: Python function decorator | rookie tutorial (runoob.com)

Guess you like

Origin blog.csdn.net/qq_52852138/article/details/128771384