We often come across this need during programming:
For example, if I want to develop a calculator, and I've written a bunch of functions to perform various calculations, theWe need to check the input data before executing the various calculation functions to make sure that they have to be numeric values in order to be allowed to execute the function.and cannot be a string;
As another example, I want to write a module for calculating the perimeter, area, and angle of a certain angle of a triangle, and I have already written several functions for the calculation, so theBefore performing the calculation, first make sure that the three side lengths entered can form a triangle, and then do the calculations to make sense;
Then, for example, if I want to develop a certain web application and write some functions for implementing certain actions by the user, then theYou have to check that the user is logged in before you are allowed to perform these actions.。
These needs, in a nutshell, are.A pre-function is often executed before the main function is executed, do some checksums and such.
This type of requirement is very common and important to ensure the integrity and robustness of the program. So, how is it easier to do this?
You'll say, "That's easy, just write if statements inside each function". Take that calculator, if we want to write addition, subtraction, multiplication and division, we can do it like this:
def plus(a,b): if type(a)==type(0) and type(b)==type(0): # Assume that the calculator can only calculate integers, if you want to calculate decimals then or type(0.0) return a+b else: print('Type must be number') # Detect the data type is not correct, first output alarm, function value return None return None def minus(a,b): if type(a)==type(0) and type(b)==type(0): return a-b else: print('Type must be number') return None def multiply(a,b): if type(a)==type(0) and type(b)==type(0): return a*b else: print('Type must be number') return None def divide(a,b): if type(a)==type(0) and type(b)==type(0): return a/b else: print('Type must be number') return None
Well, this is straightforward and violent. But then, there are only four functions here.If you develop a calculator with dozens or hundreds of functions, each of which has to be covered by an if statement, it's going to be a pain in the ass, and not annoying, but also verbose.。
So how to make it simpler? As the clever among you must have thought, we can define a separate function for that judgment if as well, and then nest the function used for the calculation inside it, like this:
def check(a,b,func): # Define the checking function with variables for the parameters a,b to be checked and the function func to be executed after the checking passes. if type(a)==type(0) and type(b)==type(0): return func(a,b) else: print('Type must be number') return None def plus(a,b): return a+b def minus(a,b): return a-b ... # Main program check(1,2,plus) #Calculate 1+2 check(1,2,minus) # Calculation 1-2 check(1,2,multiply) # Calculate 1*2 check(1,2,divide) # Calculate 1/2
There is one point that must be paid special attention to, the main program check(1,2,plus) is the plus function itself as a variable passed to check, the check function to decide how to execute the plus function, here can not be written as check(1,2,plus(1,2)), plus can not be with parameters and parentheses, is not the result of the execution of the plus() after passing the The result is passed to check.
It's a much cleaner program.Addition, subtraction, multiplication, and division functions only need to define their own arithmetic; variable detection is left to the check function. It's also easier to understand when it's written that way.
But not so much for the users of the program, theThey'll find it very hard to read.。
Why? I'm taking the program foraddition, subtraction, multiplication and divisionCalculated, but I don't care what I calculate.The function check is called in the main call every time.!
So is there any way to look good and be concise at the same time? Decorators do that magic.
The requirement above, with a decorator can be written like this:
def check(func): ... @check def plus(a,b): return a+b @check def minus(a,b): return a-b ... # Main program plus(1,2) #Calculate 1+2 minus(1,2) # Calculation 1-2 ...
Visualize it first.With @check, the check function is "injected" into the plus function.This makes the plus function a parameter checking function. Thus, in the main program, if you want to calculate the addition, you can call plus directly, and then you can check before calculating.
So, how is this decorator check going to be defined? Let's take a look.
def check(func): # Define the decorator check def newfunc(a,b): #define function templates, i.e. how to handle func if type(a)==type(0) and type(b)==type(0): return func(a,b) else: print('Type must be number!') return None return newfunc # Output the processed func as a new function newfunc @check def plus(a,b): return a+b # Main program, calculate 1+2 plus(1,2)
As we can see.When the decorator @check acts on the plus function, the plus function itself is passed into the decorator as the argument func. Inside the definition of the decorator check, a function template is defined that describes what to do with the input func. As you can see, newfunc applies an if statement that determines the data type to func (that is, the input plus).Finally, the nested newfunc is output again, replacing the original func. Thus, at this point, executing func is executing newfunc, and executing plus is executing the new function with the if statement over it.
So.With the decorator, a new function with a judgment statement replaces the original plus function, but it is still called by the function name plus.The plus function is "decorated", so it looks like the plus function is "decorated".
Of course, if one searches the web, about how to define a decorator, one sees a more standardized version. It looks a little harder to understand, but it's really the same thing:
def checkall(func): def wrapper(*args,**kwargs): if type(args[0])==type(0) and type(args[1])==type(0): return func(*args,**kwargs) else: print('Type must be number!') return None return wrapper
Template function is generally accustomed to use wrapper to indicate that this is nothing, it is recommended that we all write so, standardize some.
Arguments are generally represented by indeterminate length *args,**kwargs, and this may be confusing to some people. Because there may be many kinds of decorated functions, and the number of arguments is generally indeterminate. Then *args,**kwargs what is it? args, kwargs the two form of the letters of the alphabet does not matter what you can set, the key is the front of the single asterisk * and double asterisk **.
Suppose I define a function that cannot determine how many arguments there are, for example to do a concatenated addition operation on a set of numbers entered. Then you can define plus(*x), when the function is called, if more than one variable plus(1,2,3) is entered, then the input variables will be combined into a single meta ancestor x=(1,2,3) input. Defining the double asterisk plus(**x) means that if you write the formal parameter variable plus(a=1,b=2,c=3) when calling this function, then the input variables are combined into the dictionary x={a:1,b:2,c:3} passed into the function.
Of course, you can also reverse the operation, define the function when the number of parameters is explicit plus(a,b,c), then call the function with an asterisk plus(*(1,2,3)), that is, the input meta-ancestor (1,2,3) to perform a blow-up operation, converted to plus(1,2,3) input.
What's the point of writing this in a decorator? Let's take a closer look at the newfunc(a,b) we wrote earlier, which means that it specifies that the new function has two arguments, a and b. What if the original function being decorated had three arguments? Wouldn't that be useless?
Let's look at what others have written, defined with wrapper(*args,**kwargs), i.e., no matter how many parameters, packaged into the wrapper. among the wrapper, call the original function and func(*args,**kwargs), i.e., the input meta-ancestor unpacking and then pass into the func.This packing and unpacking, although it seems to do nothing, does adapt to the uncertainty of the function parameters, so that the decorator can decorate a variety of functions with a different number of parameters。
That's all for now.
To this point this article on Python @ decorator is what to do? The article is introduced to this, more related Python @ decorator content please search for my previous posts or continue to browse the following related articles I hope you will support me more in the future!