Catalog
Decorator Advancement
As you already know from the previous article, if you still don't understand what decoration is, please go back. Introduction to Decorator . The first one is an introduction to decoration. Decorator has a very important application in python language. From script development to web framework development with many functions. As we know, the decorated functions we have seen before are the simplest function format. The simplicity here refers to the form of a function without parameters, or that the function does not return (directly execute the result) values. The decorated functions in the actual development are not so simple format as we can see. More complex functions such as multiple return values, multiple parameters (formal parameters, keyword parameters), so this article is a step forward of python decorator.
1. Decorated functions have multiple parameters.
Scenario 1: A Simple Authentication Decorator
-
Example code
from functools import wraps def auth2(func): @wraps(func) def wrapper(*arg, **kwargs): user = input("pls input password:").strip() if user == "faily": print("---welcome login----") func(*arg, **kwargs) else: print("----wrong password----") return wrapper @auth2 def task2(name, is_happy="Yes"): print("{0} is doing somthing, Is he happy? [{1}]".format(name, is_happy)) if __name__ =="__main__": '''Decorator with parameters''' task2("faily")
As you can see, the decorated function task2 has two parameters, name, is_happy, so how to pass the two parameters into the decorator function?
- According to the previous knowledge, we can see that when the decorator is executed, it will first pass the decorated function name task2 to the decorator function auth2. Then the parameters of the decorated function have to be passed in somewhere else. The only place that can accept these variables is the wrapper function.
- After the parameters are passed in through wrapper function, the parameters can be transferred to a decorated function.
2. Decorated functions have return values
-
Obviously, this function is the most common one. It shows the result immediately after execution, but passes it as a value.
from functools import wraps def auth3(func): @wraps(func) def wrapper(arg): user = input("pls input password:").strip() if user == "faily": print("---welcome login----") return func(arg) else: print("----wrong password---") return wrapper @auth3 def task3(name): print("do somthing ") return name if __name__ == "__main__": ts = task3("momo") print(ts)
It's easy to understand that if a function has a return value, the decorated function will be returned by using the keyword return directly after the new function is executed in the wrapper function.
3. Embedding decorators in functions
Here we can understand as follows: since the decorator is a high-order function + nested function = decorator, can we have this combination, that is, the form of nested function + decorator function?
The answer is yes.
In fact, this application can also be understood as: before we wrote all the decorator functions without parameters, since the essence of the decorator is also a function, then can the decorator function with parameters? What's the format?
This is illustrated by the following examples
-
Go back to the log example and create a wrapper function that lets us specify a log file for output
from functools import wraps def logit(logfile='out.log'): # Decorator def logging_decorator(func): @wraps(func) def wrapped_function(*args, **kwargs): log_string = func.__name__ + " was called" print(log_string) # Open logfile and write content with open(logfile, 'a') as f: # Now type the log to the specified logfile f.write(log_string + '\n') return func(*args, **kwargs) return wrapped_function return logging_decorator #Default Decorator Parameters @logit() def myfunc1(action="run"): if action == "run": return True else: return False #Decorator Reference @logit(logfile='func2.log') def myfunc2(action = "stop"): if action == "run": return True else: return False if __name__ == "__main__": myfunc1() myfunc2() # Now a file called func2.log appears, which contains the string above.
In this example, do you think the decoration is too powerful?
By giving different parameters to the decorator, we can realize the new function of log output of different functions. We just add a layer of functions to the standard decorator. This function is actually used to transfer the parameters of the decorator into the package function.
4. Decorators
Scenario analysis: In operation and maintenance monitoring, we often need to record logs at different levels or generated by different apps, but when some parts of our application are still fragile, triggering exceptions may be something that needs more attention. For example, sometimes we just want to log to a file; sometimes you want to send an email when an exception occurs, and keep a log at the same time. This is a scenario using inheritance, but so far we have only seen the functions used to build the decorator.
Fortunately, classes can also be used to build decorators. So now we're rebuilding logit in the form of a class rather than a function.
-
Decorator class
rom functools import wraps class logit(object): '''Decorator class''' def __init__(self, logfile='out.log'): self.logfile = logfile def __call__(self, func): # _ call_ indicates that this is a callable @wraps(func) def wrapped_function(*args, **kwargs): log_string = func.__name__ + " was called" print(log_string) # Open logfile and write with open(self.logfile, 'a') as f: # Now type the log to the specified file f.write(log_string + '\n') # Now, send a notification self.notify() return func(*args, **kwargs) return wrapped_function def notify(self): # logit logs, nothing else. pass
An additional advantage of this implementation is that it is cleaner than nested functions, and that wrapping a function uses the same syntax as before:
@logit() def myfunc1(): pass
Now let's create subclasses for logit to add email functionality.
class email_logit(logit): ''' //An implementation version of logit that can send an email to the administrator when a function is called ''' def __init__(self, email='admin@myproject.com', *args, **kwargs): self.email = email super(email_logit, self).__init__(*args, **kwargs) # Integrated parent class def notify(self): # Send an email to self.email # It's not done here. pass
From now on, @email_logit will have the same effect as @logit, but on the basis of the output log, an additional email will be sent to the administrator.
summary
Decoration Advancement has been finished, the next article we continue to study, multi-decoration