Decorators are an important part of Python. In simple terms: they are functions that modify the functionality of other functions. They help make our code shorter and more Pythonic. Most beginners don't know where to use them, so I'm going to share some areas where decorators can make your code cleaner. First, let's discuss how to write your own decorators.
This is probably one of the hardest concepts to master. We will discuss one step at a time so that you can fully understand it.
Everything is an object
First, let's understand functions in Python:
Define functions inside functions
Those were the basics of functions. Let's take your knowledge a step further. In Python, we can define one function inside another function:
Now we know that we can define other functions inside a function. That is to say: we can create nested functions. Now you need to learn a bit more: functions can also return functions.
Returning functions from functions
Actually, there's no need to execute another function within a function; we can also return it as output:
Look at this code again. In the if/else statement we return greet and welcome, not greet() and welcome(). Why is that? It's because when you put a pair of parentheses after it, the function gets executed; however, if you don't put parentheses after it, it can be passed around and assigned to other variables without executing it. Do you understand? Let me explain a bit more in detail.
When we writea = hi(),hi()it gets executed, and since the name parameter defaults to yasoob, the functiongreet()is returned.
-
We can also print outhi()(), which will output:now you are in the greet() function。
-
If we change the statement toa = hi(name = "ali"), thenwelcome()the function will be returned.
Passing a function as an argument to another function
Now you have all the necessary knowledge to learn what decorators really are. Decorators let you execute code before and after a function.
Your first decorator
In the previous example, we actually already created a decorator! Now let's modify the previous decorator and write a slightly more useful program:
Do you see it? We just applied the principles we learned earlier. This is exactly what decorators do in Python! They wrap a function and modify its behavior in one way or another. Now you might be wondering, in our code we didn't use the@symbol? That is just a shorthand way to generate a decorated function. Here is how we use@to run the previous code:
Hopefully you now have a basic understanding of how Python decorators work. There is a problem if we run the following code:
print(a_function_requiring_decoration.__name__) # Output: wrapTheFunction
This is not what we wanted! The output should be "a_function_requiring_decoration". Here the function was replaced by warpTheFunction. It overwrote our function's name and docstring. Fortunately, Python provides us with a simple function to solve this problem, namely functools.wraps. Let's modify the previous example to use functools.wraps:
That's much better now. Next, let's learn some common use cases of decorators.
Blueprint specification:
Note:@wrapsIt accepts a function to decorate, and adds functionality to copy the function name, docstring, argument list, and so on. This allows us to access the attributes of the function before decoration inside the decorator.
Use cases
Now let's look at where decorators really shine, and how using them makes managing some things easier.
Authorization
Decorators can help check whether someone is authorized to use an endpoint of a web application. They are heavily used in Flask and Django web frameworks. Here is an example of using decorator-based authorization:
Logging
Logging is another highlight of decorator usage. Here is an example:
I'm sure you're already thinking of another clever use of decorators.
Decorators with parameters
Think about this: isn't @wraps also a decorator? But it takes a parameter, just like any ordinary function can. So why don't we do that too? That's because when you use the @my_decorator syntax, you are applying a wrapper function that takes a single function as an argument. Remember, everything in Python is an object, and that includes functions! With that in mind, we can write a function that returns a wrapper function.
Embedding decorators in functions
Let's go back to the logging example and create a wrapper function that allows us to specify a log file for output.
Decorator classes
Now we have a logit decorator suitable for production, but when certain parts of our application are still fragile, exceptions may require more urgent attention. For example, sometimes you just want to log to a file. Other times, you want to send a problem that catches your attention to an email while also keeping a log for the record. This is a scenario for using inheritance, but so far we have only seen functions used to build decorators.
Fortunately, classes can also be used to build decorators. So now let's rebuild logit using a class rather than a function.
This implementation has the additional advantage of being cleaner than the nested function approach, and wrapping a function still uses the same syntax as before:
@logit()
def myfunc1():
pass
Now, let's create a subclass of logit to add email functionality (although the email topic won't be expanded here).
From now on, @email_logit will have the same effect as @logit, but in addition to logging, it will also send an email to the administrator.
Original article address: https://eastlakeside.gitbooks.io/interpy-zh/content/decorators/