In this tutorial on decorators, we’ll look at what they are and how to create and use them. Decorators provide a simple syntax for calling higher-order functions.
By definition, a decorator is a function that takes another function and extends the behavior of the latter function without explicitly modifying it.
This sounds confusing, but it’s really not, especially after you’ve seen a few examples of how decorators work. You can find all the examples from this article here.
Free Bonus: Click here to get access to a free "The Power of Python Decorators" guide that shows you three advanced decorator patterns and techniques you can use to write cleaner and more Pythonic programs.
Decorators Cheat Sheet: Click here to get access to a free three-page Python decorators cheat sheet that summarizes the techniques explained in this tutorial.
Decorators Q&A Transcript: Click here to get access to a 25-page chat log from our Python decorators Q&A session in the Real Python Community Slack where we discussed common decorator questions.
Updates:
08/22/2018: Major update adding more examples and more advanced decorators
01/12/2016: Updated examples to Python 3 (v3.5.1) syntax and added a new example
11/01/2015: Added a brief explanation on the
defmy_decorator(func):defwrapper():print("Something is happening before the function is called.")func()print("Something is happening after the function is called.")returnwrapperdefsay_whee():print("Whee!")say_whee=my_decorator(say_whee)
7 decorator
Functions
Before you can understand decorators, you must first understand how functions work. For our purposes, a function returns a value based on the given arguments. Here is a very simple example:
In general, functions in Python may also have side effects rather than just turning an input into an output. The
defmy_decorator(func):defwrapper():print("Something is happening before the function is called.")func()print("Something is happening after the function is called.")returnwrapperdefsay_whee():print("Whee!")say_whee=my_decorator(say_whee)
8 function is a basic example of this: it returns
defmy_decorator(func):defwrapper():print("Something is happening before the function is called.")func()print("Something is happening after the function is called.")returnwrapperdefsay_whee():print("Whee!")say_whee=my_decorator(say_whee)
9 while having the side effect of outputting something to the console. However, to understand decorators, it is enough to think about functions as something that turns given arguments into a value.
Note: In functional programming, you work (almost) only with pure functions without side effects. While not a purely functional language, Python supports many of the functional programming concepts, including functions as first-class objects.
Remove ads
First-Class Objects
In Python, functions are first-class objects. This means that functions can be passed around and used as arguments, just like any other object (string, int, float, list, and so on). Consider the following three functions:
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
Here,
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
00 and
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
01 are regular functions that expect a name given as a string. The
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
02 function however, expects a function as its argument. We can, for instance, pass it the
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
00 or the
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
01 function:
>>>
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
Note that
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
05 refers to two functions, but in different ways:
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
02 and
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
07. The
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
07 function is named without parentheses. This means that only a reference to the function is passed. The function is not executed. The
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
02 function, on the other hand, is written with parentheses, so it will be called as usual.
Inner Functions
It’s possible to define functions inside other functions. Such functions are called inner functions. Here’s an example of a function with two inner functions:
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
What happens when you call the
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
10 function? Think about this for a minute. The output will be as follows:
>>>
>>> parent()Printing from the parent() functionPrinting from the second_child() functionPrinting from the first_child() function
Note that the order in which the inner functions are defined does not matter. Like with any other functions, the printing only happens when the inner functions are executed.
Furthermore, the inner functions are not defined until the parent function is called. They are locally scoped to
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
10: they only exist inside the
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
10 function as local variables. Try calling
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
13. You should get an error:
Traceback (most recent call last):
File "", line 1, in NameError: name 'first_child' is not defined
Whenever you call
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
10, the inner functions
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
13 and
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
16 are also called. But because of their local scope, they aren’t available outside of the
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
10 function.
Returning Functions From Functions
Python also allows you to use functions as return values. The following example returns one of the inner functions from the outer
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
10 function:
defparent(num):deffirst_child():return"Hi, I am Emma"defsecond_child():return"Call me Liam"ifnum==1:returnfirst_childelse:returnsecond_child
Note that you are returning
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
19 without the parentheses. Recall that this means that you are returning a reference to the function
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
19. In contrast
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
13 with parentheses refers to the result of evaluating the function. This can be seen in the following example:
>>>
>>> first=parent(1)>>> second=parent(2)>>> first.first_child at 0x7f599f1e2e18>>>> second.second_child at 0x7f599dad5268>
The somewhat cryptic output simply means that the
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
22 variable refers to the local
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
13 function inside of
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
10, while
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
25 points to
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
16.
You can now use
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
22 and
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
25 as if they are regular functions, even though the functions they point to can’t be accessed directly:
>>>
>>> first()'Hi, I am Emma'>>> second()'Call me Liam'
Finally, note that in the earlier example you executed the inner functions within the parent function, for instance
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
13. However, in this last example, you did not add parentheses to the inner functions—
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
19—upon returning. That way, you got a reference to each function that you could call in the future. Make sense?Remove ads
Simple Decorators
Now that you’ve seen that functions are just like any other object in Python, you’re ready to move on and see the magical beast that is the Python decorator. Let’s start with an example:
defmy_decorator(func):defwrapper():print("Something is happening before the function is called.")func()print("Something is happening after the function is called.")returnwrapperdefsay_whee():print("Whee!")say_whee=my_decorator(say_whee)
Can you guess what happens when you call
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
31? Try it:
>>>
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
0
To understand what’s going on here, look back at the previous examples. We are literally just applying everything you have learned so far.
The so-called decoration happens at the following line:
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
1
In effect, the name
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
32 now points to the
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
33 inner function. Remember that you return
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
34 as a function when you call
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
35:
>>>
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
2
However,
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
33 has a reference to the original
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
31 as
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
38, and calls that function between the two calls to
defmy_decorator(func):defwrapper():print("Something is happening before the function is called.")func()print("Something is happening after the function is called.")returnwrapperdefsay_whee():print("Whee!")say_whee=my_decorator(say_whee)
8.
Put simply: decorators wrap a function, modifying its behavior.
Before moving on, let’s have a look at a second example. Because
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
33 is a regular Python function, the way a decorator modifies a function can change dynamically. So as not to disturb your neighbors, the following example will only run the decorated code during the day:
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
3
If you try to call
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
31 after bedtime, nothing will happen:
>>>
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
4
Syntactic Sugar!
The way you decorated
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
31 above is a little clunky. First of all, you end up typing the name
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
32 three times. In addition, the decoration gets a bit hidden away below the definition of the function.
Instead, Python allows you to use decorators in a simpler way with the
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
44 symbol, sometimes called the . The following example does the exact same thing as the first decorator example:
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
5
So,
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
45 is just an easier way of saying
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
46. It’s how you apply a decorator to a function.Remove ads
Reusing Decorators
Recall that a decorator is just a regular Python function. All the usual tools for easy reusability are available. Let’s move the decorator to its own module that can be used in many other functions.
Create a file called
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
47 with the following content:
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
6
Note: You can name your inner function whatever you want, and a generic name like
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
33 is usually okay. You’ll see a lot of decorators in this article. To keep them apart, we’ll name the inner function with the same name as the decorator but with a
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
49 prefix.
You can now use this new decorator in other files by doing a regular import:
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
7
When you run this example, you should see that the original
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
31 is executed twice:
>>>
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
8
Free Bonus: Click here to get access to a free "The Power of Python Decorators" guide that shows you three advanced decorator patterns and techniques you can use to write cleaner and more Pythonic programs.
Decorating Functions With Arguments
Say that you have a function that accepts some arguments. Can you still decorate it? Let’s try:
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
9
Unfortunately, running this code raises an error:
>>>
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
0
The problem is that the inner function
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
51 does not take any arguments, but
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
52 was passed to it. You could fix this by letting
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
51 accept one argument, but then it would not work for the
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
31 function you created earlier.
The solution is to use
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
55 and
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
56 in the inner wrapper function. Then it will accept an arbitrary number of positional and keyword arguments. Rewrite
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
47 as follows:
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
1
The
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
51 inner function now accepts any number of arguments and passes them on to the function it decorates. Now both your
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
31 and
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
60 examples works:
>>>
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
2Remove ads
Returning Values From Decorated Functions
What happens to the return value of decorated functions? Well, that’s up to the decorator to decide. Let’s say you decorate a simple function as follows:
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
3
Try to use it:
>>>
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
4
Oops, your decorator ate the return value from the function.
Because the
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
61 doesn’t explicitly return a value, the call
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
62 ended up returning
defmy_decorator(func):defwrapper():print("Something is happening before the function is called.")func()print("Something is happening after the function is called.")returnwrapperdefsay_whee():print("Whee!")say_whee=my_decorator(say_whee)
9.
To fix this, you need to make sure the wrapper function returns the return value of the decorated function. Change your
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
47 file:
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
5
The return value from the last execution of the function is returned:
>>>
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
6
Who Are You, Really?
A great convenience when working with Python, especially in the interactive shell, is its powerful introspection ability. Introspection is the ability of an object to know about its own attributes at runtime. For instance, a function knows its own name and documentation:
>>>
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
7
The introspection works for functions you define yourself as well:
>>>
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
8
However, after being decorated,
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
31 has gotten very confused about its identity. It now reports being the
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
51 inner function inside the
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
67 decorator. Although technically true, this is not very useful information.
To fix this, decorators should use the decorator, which will preserve information about the original function. Update
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
47 again:
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
9
You do not need to change anything about the decorated
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
31 function:
>>>
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
0
Much better! Now
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
31 is still itself after decoration.
Technical Detail: The
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
68 decorator the function
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
73 to update special attributes like
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
74 and
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
75 that are used in the introspection.Remove ads
A Few Real World Examples
Let’s look at a few more useful examples of decorators. You’ll notice that they’ll mainly follow the same pattern that you’ve learned so far:
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
1
This formula is a good boilerplate template for building more complex decorators.
Note: In later examples, we will assume that these decorators are saved in your
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
47 file as well. Recall that you can download all the examples in this tutorial.
Timing Functions
Let’s start by creating a
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
77 decorator. It will measure the time a function takes to execute and print the duration to the console. Here’s the code:
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
2
This decorator works by storing the time just before the function starts running (at the line marked
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
78) and just after the function finishes (at
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
79). The time the function takes is then the difference between the two (at
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
80). We use the function, which does a good job of measuring time intervals. Here are some examples of timings:
>>>
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
3
Run it yourself. Work through the code line by line. Make sure you understand how it works. Don’t worry if you don’t get it, though. Decorators are advanced beings. Try to sleep on it or make a drawing of the program flow.
Note: The
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
77 decorator is great if you just want to get an idea about the runtime of your functions. If you want to do more precise measurements of code, you should instead consider the
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
83 module in the standard library. It temporarily disables and runs multiple trials to strip out noise from quick function calls.
Debugging Code
The following
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
84 decorator will print the arguments a function is called with as well as its return value every time the function is called:
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
4
The signature is created by joining the string representations of all the arguments. The numbers in the following list correspond to the numbered comments in the code:
Create a list of the positional arguments. Use
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
85 to get a nice string representing each argument.
Create a list of the keyword arguments. The f-string formats each argument as
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
86 where the
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
87 specifier means that
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
85 is used to represent the value.
The lists of positional and keyword arguments is joined together to one signature string with each argument separated by a comma.
The return value is printed after the function is executed.
Let’s see how the decorator works in practice by applying it to a simple function with one position and one keyword argument:
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
5
Note how the
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
84 decorator prints the signature and return value of the
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
90 function:
>>>
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
6
This example might not seem immediately useful since the
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
84 decorator just repeats what you just wrote. It’s more powerful when applied to small convenience functions that you don’t call directly yourself.
The following example calculates an approximation to the mathematical constant e:
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
7
This example also shows how you can apply a decorator to a function that has already been defined. The approximation of e is based on the following series expansion:
When calling the
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
92 function, you can see the
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
84 decorator at work:
>>>
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
8
In this example, you get a decent approximation to the true value e = 2.718281828, adding only 5 terms.
Remove ads
Slowing Down Code
This next example might not seem very useful. Why would you want to slow down your Python code? Probably the most common use case is that you want to rate-limit a function that continuously checks whether a resource—like a web page—has changed. The
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
94 decorator will sleep one second before it calls the decorated function:
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
9
To see the effect of the
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
94 decorator, you really need to run the example yourself:
>>>
>>> parent()Printing from the parent() functionPrinting from the second_child() functionPrinting from the first_child() function
0
Note: The
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
96 function is a recursive function. In other words, it’s a function calling itself. To learn more about recursive functions in Python, see our guide on Thinking Recursively in Python.
The
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
94 decorator always sleeps for one second. , you’ll see how to control the rate by passing an argument to the decorator.
Registering Plugins
Decorators don’t have to wrap the function they’re decorating. They can also simply register that a function exists and return it unwrapped. This can be used, for instance, to create a light-weight plug-in architecture:
>>> parent()Printing from the parent() functionPrinting from the second_child() functionPrinting from the first_child() function
1
The
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
98 decorator simply stores a reference to the decorated function in the global
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
99 dict. Note that you do not have to write an inner function or use
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
68 in this example because you are returning the original function unmodified.
The
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
01 function randomly chooses one of the registered functions to use. Note that the
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
99 dictionary already contains references to each function object that is registered as a plugin:
>>>
>>> parent()Printing from the parent() functionPrinting from the second_child() functionPrinting from the first_child() function
2
The main benefit of this simple plugin architecture is that you do not need to maintain a list of which plugins exist. That list is created when the plugins register themselves. This makes it trivial to add a new plugin: just define the function and decorate it with
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
98.
If you are familiar with
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
04 in Python, you might see some similarities to how the plugin architecture works.
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
04 gives access to all global variables in the current scope, including your plugins:
>>>
>>> parent()Printing from the parent() functionPrinting from the second_child() functionPrinting from the first_child() function
3
Using the
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
98 decorator, you can create your own curated list of interesting variables, effectively hand-picking some functions from
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
04.
Is the User Logged In?
The final example before moving on to some fancier decorators is commonly used when working with a web framework. In this example, we are using Flask to set up a
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
08 web page that should only be visible to users that are logged in or otherwise authenticated:
>>> parent()Printing from the parent() functionPrinting from the second_child() functionPrinting from the first_child() function
4
While this gives an idea about how to add authentication to your web framework, you should usually not write these types of decorators yourself. For Flask, you can use instead, which adds more security and functionality.
Remove ads
Fancy Decorators
So far, you’ve seen how to create simple decorators. You already have a pretty good understanding of what decorators are and how they work. Feel free to take a break from this article to practice everything you’ve learned.
In the second part of this tutorial, we’ll explore more advanced features, including how to use the following:
Decorating Classes
There are two different ways you can use decorators on classes. The first one is very close to what you have already done with functions: you can decorate the methods of a class. This was for introducing decorators back in the day.
Some commonly used decorators that are even built-ins in Python are
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
09,
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
10, and
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
11. The
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
09 and
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
10 decorators are used to define methods inside a class namespace that are not connected to a particular instance of that class. The
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
11 decorator is used to customize getters and setters for class attributes. Expand the box below for an example using these decorators.
Example using built-in class decoratorsShow/Hide
The following definition of a
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
15 class uses the
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
09,
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
10, and
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
11 decorators:
>>> parent()Printing from the parent() functionPrinting from the second_child() functionPrinting from the first_child() function
5
In this class:
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
19 is a regular method.
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
20 is a mutable property: it can be set to a different value. However, by defining a setter method, we can do some error testing to make sure it’s not set to a nonsensical negative number. Properties are accessed as attributes without parentheses.
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
21 is an immutable property: properties without
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
22 methods can’t be changed. Even though it is defined as a method, it can be retrieved as an attribute without parentheses.
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
23 is a class method. It’s not bound to one particular instance of
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
15. Class methods are often used as factory methods that can create specific instances of the class.
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
25 is a static method. It’s not really dependent on the
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
15 class, except that it is part of its namespace. Static methods can be called on either an instance or the class.
The
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
15 class can for example be used as follows:
>>>
>>> parent()Printing from the parent() functionPrinting from the second_child() functionPrinting from the first_child() function
6
Let’s define a class where we decorate some of its methods using the and decorators from :
>>> parent()Printing from the parent() functionPrinting from the second_child() functionPrinting from the first_child() function
7
Using this class, you can see the effect of the decorators:
>>>
>>> parent()Printing from the parent() functionPrinting from the second_child() functionPrinting from the first_child() function
8
The other way to use decorators on classes is to decorate the whole class. This is, for example, done in the new
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
30 module in Python 3.7:
>>> parent()Printing from the parent() functionPrinting from the second_child() functionPrinting from the first_child() function
9
The meaning of the syntax is similar to the function decorators. In the example above, you could have done the decoration by writing
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
31.
A is to be a simpler alternative to some use-cases of metaclasses. In both cases, you are changing the definition of a class dynamically.
Writing a class decorator is very similar to writing a function decorator. The only difference is that the decorator will receive a class and not a function as an argument. In fact, all the decorators will work as class decorators. When you are using them on a class instead of a function, their effect might not be what you want. In the following example, the
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
77 decorator is applied to a class:
Traceback (most recent call last):
File "", line 1, in NameError: name 'first_child' is not defined
0
Decorating a class does not decorate its methods. Recall that
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
77 is just shorthand for
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
34.
Here,
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
77 only measures the time it takes to instantiate the class:
>>>
Traceback (most recent call last):
File "", line 1, in NameError: name 'first_child' is not defined
1
, you will see an example defining a proper class decorator, namely
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
36, which ensures that there is only one instance of a class.Remove ads
Nesting Decorators
You can apply several decorators to a function by stacking them on top of each other:
Traceback (most recent call last):
File "", line 1, in NameError: name 'first_child' is not defined
2
Think about this as the decorators being executed in the order they are listed. In other words,
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
84 calls
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
38, which calls
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
60, or
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
40:
>>>
Traceback (most recent call last):
File "", line 1, in NameError: name 'first_child' is not defined
3
Observe the difference if we change the order of
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
84 and
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
38:
Traceback (most recent call last):
File "", line 1, in NameError: name 'first_child' is not defined
4
In this case,
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
38 will be applied to
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
84 as well:
>>>
Traceback (most recent call last):
File "", line 1, in NameError: name 'first_child' is not defined
5
Decorators With Arguments
Sometimes, it’s useful to pass arguments to your decorators. For instance,
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
38 could be extended to a
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
46 decorator. The number of times to execute the decorated function could then be given as an argument.
This would allow you to do something like this:
Traceback (most recent call last):
File "", line 1, in NameError: name 'first_child' is not defined
6
>>>
Traceback (most recent call last):
File "", line 1, in NameError: name 'first_child' is not defined
7
Think about how you could achieve this.
So far, the name written after the
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
44 has referred to a function object that can be called with another function. To be consistent, you then need
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
48 to return a function object that can act as a decorator. Luckily, you ! In general, you want something like the following:
Traceback (most recent call last):
File "", line 1, in NameError: name 'first_child' is not defined
8
Typically, the decorator creates and returns an inner wrapper function, so writing the example out in full will give you an inner function within an inner function. While this might sound like the programming equivalent of the Inception movie, we’ll untangle it all in a moment:
Traceback (most recent call last):
File "", line 1, in NameError: name 'first_child' is not defined
9
It looks a little messy, but we have only put the same decorator pattern you have seen many times by now inside one additional
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
49 that handles the arguments to the decorator. Let’s start with the innermost function:
defparent(num):deffirst_child():return"Hi, I am Emma"defsecond_child():return"Call me Liam"ifnum==1:returnfirst_childelse:returnsecond_child
0
This
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
50 function takes arbitrary arguments and returns the value of the decorated function,
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
51. This wrapper function also contains the loop that calls the decorated function
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
52 times. This is no different from the earlier wrapper functions you have seen, except that it is using the
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
52 parameter that must be supplied from the outside.
One step out, you’ll find the decorator function:
defparent(num):deffirst_child():return"Hi, I am Emma"defsecond_child():return"Call me Liam"ifnum==1:returnfirst_childelse:returnsecond_child
1
Again,
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
54 looks exactly like the decorator functions you have written earlier, except that it’s named differently. That’s because we reserve the base name—
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
55—for the outermost function, which is the one the user will call.
As you have already seen, the outermost function returns a reference to the decorator function:
defparent(num):deffirst_child():return"Hi, I am Emma"defsecond_child():return"Call me Liam"ifnum==1:returnfirst_childelse:returnsecond_child
2
There are a few subtle things happening in the
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
55 function:
Defining
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
54 as an inner function means that
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
55 will refer to a function object—
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
59. Earlier, we used
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
60 without parentheses to refer to the function object. The added parentheses are necessary when defining decorators that take arguments.
The
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
52 argument is seemingly not used in
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
55 itself. But by passing
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
52 a closure is created where the value of
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
52 is stored until it will be used later by
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
50.
With everything set up, let’s see if the results are as expected:
Traceback (most recent call last):
File "", line 1, in NameError: name 'first_child' is not defined
6
>>>
Traceback (most recent call last):
File "", line 1, in NameError: name 'first_child' is not defined
7
Just the result we were aiming for.
Remove ads
Both Please, But Never Mind the Bread
With a little bit of care, you can also define decorators that can be used both with and without arguments. Most likely, you don’t need this, but it is nice to have the flexibility.
As you saw in the previous section, when a decorator uses arguments, you need to add an extra outer function. The challenge is for your code to figure out if the decorator has been called with or without arguments.
Since the function to decorate is only passed in directly if the decorator is called without arguments, the function must be an optional argument. This means that the decorator arguments must all be specified by keyword. You can enforce this with the special
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
66 syntax, which means that all following parameters are keyword-only:
defparent(num):deffirst_child():return"Hi, I am Emma"defsecond_child():return"Call me Liam"ifnum==1:returnfirst_childelse:returnsecond_child
5
Here, the
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
67 argument acts as a marker, noting whether the decorator has been called with arguments or not:
If
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
68 has been called without arguments, the decorated function will be passed in as
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
67. If it has been called with arguments, then
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
67 will be
defmy_decorator(func):defwrapper():print("Something is happening before the function is called.")func()print("Something is happening after the function is called.")returnwrapperdefsay_whee():print("Whee!")say_whee=my_decorator(say_whee)
9, and some of the keyword arguments may have been changed from their default values. The
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
66 in the argument list means that the remaining arguments can’t be called as positional arguments.
In this case, the decorator was called with arguments. Return a decorator function that can read and return a function.
In this case, the decorator was called without arguments. Apply the decorator to the function immediately.
Using this boilerplate on the
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
73 decorator in the previous section, you can write the following:
defparent(num):deffirst_child():return"Hi, I am Emma"defsecond_child():return"Call me Liam"ifnum==1:returnfirst_childelse:returnsecond_child
6
Compare this with the original
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
73. The only changes are the added
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
67 parameter and the
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
76-
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
77 at the end.
Recipe 9.6 of the excellent Python Cookbook shows an alternative solution using .
These examples show that
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
73 can now be used with or without arguments:
defparent(num):deffirst_child():return"Hi, I am Emma"defsecond_child():return"Call me Liam"ifnum==1:returnfirst_childelse:returnsecond_child
7
Recall that the default value of
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
52 is 2:
>>>
defparent(num):deffirst_child():return"Hi, I am Emma"defsecond_child():return"Call me Liam"ifnum==1:returnfirst_childelse:returnsecond_child
8
Stateful Decorators
Sometimes, it’s useful to have a decorator that can keep track of state. As a simple example, we will create a decorator that counts the number of times a function is called.
Note: In , we talked about pure functions returning a value based on given arguments. Stateful decorators are quite the opposite, where the return value will depend on the current state, as well as the given arguments.
In the , you will see how to use classes to keep state. But in simple cases, you can also get away with using function attributes:
defparent(num):deffirst_child():return"Hi, I am Emma"defsecond_child():return"Call me Liam"ifnum==1:returnfirst_childelse:returnsecond_child
9
The state—the number of calls to the function—is stored in the function attribute
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
81 on the wrapper function. Here is the effect of using it:
>>>
>>> first=parent(1)>>> second=parent(2)>>> first.first_child at 0x7f599f1e2e18>>>> second.second_child at 0x7f599dad5268>
0Remove ads
Classes as Decorators
The typical way to maintain state is by using classes. In this section, you’ll see how to rewrite the
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
82 example from the previous section using a class as a decorator.
Recall that the decorator syntax
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
45 is just an easier way of saying
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
84. Therefore, if
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
85 is a class, it needs to take
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
38 as an argument in its
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
87 method. Furthermore, the class instance needs to be so that it can stand in for the decorated function.
For a class instance to be callable, you implement the special
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
88 method:
>>> first=parent(1)>>> second=parent(2)>>> first.first_child at 0x7f599f1e2e18>>>> second.second_child at 0x7f599dad5268>
1
The
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
88 method is executed each time you try to call an instance of the class:
>>>
>>> first=parent(1)>>> second=parent(2)>>> first.first_child at 0x7f599f1e2e18>>>> second.second_child at 0x7f599dad5268>
2
Therefore, a typical implementation of a decorator class needs to implement
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
87 and
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
88:
>>> first=parent(1)>>> second=parent(2)>>> first.first_child at 0x7f599f1e2e18>>>> second.second_child at 0x7f599dad5268>
3
The
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
87 method must store a reference to the function and can do any other necessary initialization. The
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
88 method will be called instead of the decorated function. It does essentially the same thing as the
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
33 function in our earlier examples. Note that you need to use the function instead of
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
68.
This
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
97 decorator works the same as the one in the previous section:
>>>
>>> first=parent(1)>>> second=parent(2)>>> first.first_child at 0x7f599f1e2e18>>>> second.second_child at 0x7f599dad5268>
0
More Real World Examples
We’ve come a far way now, having figured out how to create all kinds of decorators. Let’s wrap it up, putting our newfound knowledge into creating a few more examples that might actually be useful in the real world.
Slowing Down Code, Revisited
As noted earlier, our always sleeps for one second. Now you know how to add parameters to decorators, so let’s rewrite
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
94 using an optional
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
00 argument that controls how long it sleeps:
>>> first=parent(1)>>> second=parent(2)>>> first.first_child at 0x7f599f1e2e18>>>> second.second_child at 0x7f599dad5268>
5
We’re using the boilerplate introduced in the section to make
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
94 callable both with and without arguments. The same recursive
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
96 function now sleeps two seconds between each count:
>>> first=parent(1)>>> second=parent(2)>>> first.first_child at 0x7f599f1e2e18>>>> second.second_child at 0x7f599dad5268>
6
As before, you must run the example yourself to see the effect of the decorator:
>>>
>>> parent()Printing from the parent() functionPrinting from the second_child() functionPrinting from the first_child() function
0
Creating Singletons
A singleton is a class with only one instance. There are several singletons in Python that you use frequently, including
defmy_decorator(func):defwrapper():print("Something is happening before the function is called.")func()print("Something is happening after the function is called.")returnwrapperdefsay_whee():print("Whee!")say_whee=my_decorator(say_whee)
9,
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
04, and
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
05. It is the fact that
defmy_decorator(func):defwrapper():print("Something is happening before the function is called.")func()print("Something is happening after the function is called.")returnwrapperdefsay_whee():print("Whee!")say_whee=my_decorator(say_whee)
9 is a singleton that allows you to compare for
defmy_decorator(func):defwrapper():print("Something is happening before the function is called.")func()print("Something is happening after the function is called.")returnwrapperdefsay_whee():print("Whee!")say_whee=my_decorator(say_whee)
9 using the
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
08 keyword, like you saw in the section:
>>> first=parent(1)>>> second=parent(2)>>> first.first_child at 0x7f599f1e2e18>>>> second.second_child at 0x7f599dad5268>
8
Using
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
08 returns
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
04 only for objects that are the exact same instance. The following
>>> greet_bob(say_hello)'Hello Bob'>>> greet_bob(be_awesome)'Yo Bob, together we are the awesomest!'
36 decorator turns a class into a singleton by storing the first instance of the class as an attribute. Later attempts at creating an instance simply return the stored instance:
>>> first=parent(1)>>> second=parent(2)>>> first.first_child at 0x7f599f1e2e18>>>> second.second_child at 0x7f599dad5268>
9
As you see, this class decorator follows the same template as our function decorators. The only difference is that we are using
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
12 instead of
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
38 as the parameter name to indicate that it is meant to be a class decorator.
Let’s see if it works:
>>>
>>> first()'Hi, I am Emma'>>> second()'Call me Liam'
0
It seems clear that
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
14 is indeed the exact same instance as
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
15.
Note: Singleton classes are not really used as often in Python as in other languages. The effect of a singleton is usually better implemented as a global variable in a module.
Caching Return Values
Decorators can provide a nice mechanism for caching and memoization. As an example, let’s look at a recursive definition of the Fibonacci sequence:
>>> first()'Hi, I am Emma'>>> second()'Call me Liam'
1
While the implementation is simple, its runtime performance is terrible:
>>>
>>> first()'Hi, I am Emma'>>> second()'Call me Liam'
2
To calculate the tenth Fibonacci number, you should really only need to calculate the preceding Fibonacci numbers, but this implementation somehow needs a whopping 177 calculations. It gets worse quickly: 21891 calculations are needed for
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
16 and almost 2.7 million calculations for the 30th number. This is because the code keeps recalculating Fibonacci numbers that are already known.
The usual solution is to implement Fibonacci numbers using a
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
17 loop and a lookup table. However, simple caching of the calculations will also do the trick:
>>> first()'Hi, I am Emma'>>> second()'Call me Liam'
3
The cache works as a lookup table, so now
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
18 only does the necessary calculations once:
>>>
>>> first()'Hi, I am Emma'>>> second()'Call me Liam'
4
Note that in the final call to
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
19, no new calculations were needed, since the eighth Fibonacci number had already been calculated for
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
20.
In the standard library, a Least Recently Used (LRU) cache is available as .
This decorator has more features than the one you saw above. You should use
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
21 instead of writing your own cache decorator:
>>> first()'Hi, I am Emma'>>> second()'Call me Liam'
5
The
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
23 parameter specifies how many recent calls are cached. The default value is 128, but you can specify
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
24 to cache all function calls. However, be aware that this can cause memory problems if you are caching many large objects.
You can use the
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
25 method to see how the cache performs, and you can tune it if needed. In our example, we used an artificially small
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
23 to see the effect of elements being removed from the cache:
>>>
>>> first()'Hi, I am Emma'>>> second()'Call me Liam'
6
Adding Information About Units
The following example is somewhat similar to the example from earlier, in that it does not really change the behavior of the decorated function. Instead, it simply adds
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
27 as a function attribute:
>>> first()'Hi, I am Emma'>>> second()'Call me Liam'
7
The following example calculates the volume of a cylinder based on its radius and height in centimeters:
>>> first()'Hi, I am Emma'>>> second()'Call me Liam'
8
This
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
28 function attribute can later be accessed when needed:
>>>
>>> first()'Hi, I am Emma'>>> second()'Call me Liam'
9
Note that you could have achieved something similar using function annotations:
defmy_decorator(func):defwrapper():print("Something is happening before the function is called.")func()print("Something is happening after the function is called.")returnwrapperdefsay_whee():print("Whee!")say_whee=my_decorator(say_whee)
0
However, since annotations are used for type hints, it would be hard to combine such units as annotations with .
Units become even more powerful and fun when connected with a library that can convert between units. One such library is
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
29. With
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
29 installed (
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
31), you can for instance convert the volume to cubic inches or gallons:
>>>
defmy_decorator(func):defwrapper():print("Something is happening before the function is called.")func()print("Something is happening after the function is called.")returnwrapperdefsay_whee():print("Whee!")say_whee=my_decorator(say_whee)
1
You could also modify the decorator to return a
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
29
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
33 directly. Such a
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
33 is made by multiplying a value with the unit. In
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
29, units must be looked up in a
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
36. The registry is stored as a function attribute to avoid cluttering the namespace:
defmy_decorator(func):defwrapper():print("Something is happening before the function is called.")func()print("Something is happening after the function is called.")returnwrapperdefsay_whee():print("Whee!")say_whee=my_decorator(say_whee)
2
With the
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
37 decorator, converting units is practically effortless:
>>>
defmy_decorator(func):defwrapper():print("Something is happening before the function is called.")func()print("Something is happening after the function is called.")returnwrapperdefsay_whee():print("Whee!")say_whee=my_decorator(say_whee)
3
Validating JSON
Let’s look at one last use case. Take a quick look at the following Flask route handler:
defmy_decorator(func):defwrapper():print("Something is happening before the function is called.")func()print("Something is happening after the function is called.")returnwrapperdefsay_whee():print("Whee!")say_whee=my_decorator(say_whee)
4
Here we ensure that the key
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
38 is part of the request. Although this validation works, it really does not belong in the function itself. Plus, perhaps there are other routes that use the exact same validation. So, let’s keep it DRY and abstract out any unnecessary logic with a decorator. The following
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
39 decorator will do the job:
defmy_decorator(func):defwrapper():print("Something is happening before the function is called.")func()print("Something is happening after the function is called.")returnwrapperdefsay_whee():print("Whee!")say_whee=my_decorator(say_whee)
5
In the above code, the decorator takes a variable length list as an argument so that we can pass in as many string arguments as necessary, each representing a key used to validate the JSON data:
The list of keys that must be present in the JSON is given as arguments to the decorator.
The wrapper function validates that each expected key is present in the JSON data.
The route handler can then focus on its real job—updating grades—as it can safely assume that JSON data are valid:
defmy_decorator(func):defwrapper():print("Something is happening before the function is called.")func()print("Something is happening after the function is called.")returnwrapperdefsay_whee():print("Whee!")say_whee=my_decorator(say_whee)
6
Conclusion
This has been quite a journey! You started this tutorial by looking a little closer at functions, particularly how they can be defined inside other functions and passed around just like any other Python object. Then you learned about decorators and how to write them such that:
They can be reused.
They can decorate functions with arguments and return values.
They can use
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
68 to look more like the decorated function.
In the second part of the tutorial, you saw more advanced decorators and learned how to:
Decorate classes
Nest decorators
Add arguments to decorators
Keep state within decorators
Use classes as decorators
You saw that, to define a decorator, you typically define a function returning a wrapper function. The wrapper function uses
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
55 and
defsay_hello(name):returnf"Hello {name}"defbe_awesome(name):returnf"Yo {name}, together we are the awesomest!"defgreet_bob(greeter_func):returngreeter_func("Bob")
56 to pass on arguments to the decorated function. If you want your decorator to also take arguments, you need to nest the wrapper function inside another function. In this case, you usually end up with three
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
43 statements.
You can find the code from this tutorial online.
Further Reading
If you are still looking for more, our book Python Tricks has a section on decorators, as does the Python Cookbook by David Beazley and Brian K. Jones.
For a deep dive into the historical discussion on how decorators should be implemented in Python, see PEP 318 as well as the Python Decorator Wiki. More examples of decorators can be found in the Python Decorator Library. The
defparent():print("Printing from the parent() function")deffirst_child():print("Printing from the first_child() function")defsecond_child():print("Printing from the second_child() function")second_child()first_child()
44 module can simplify creating your own decorators, and its contains further decorator examples.
Also, we’ve put together a short & sweet Python decorators cheat sheet for you:
Decorators Cheat Sheet: Click here to get access to a free three-page Python decorators cheat sheet that summarizes the techniques explained in this tutorial.
Mark as Completed
Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: Python Decorators 101
🐍 Python Tricks 💌
Get a short & sweet Python Trick delivered to your inbox every couple of days. No spam ever. Unsubscribe any time. Curated by the Real Python team.
Send Me Python Tricks »
About Geir Arne Hjelle
Geir Arne is an avid Pythonista and a member of the Real Python tutorial team.
» More about Geir Arne
Each tutorial at Real Python is created by a team of developers so that it meets our high quality standards. The team members who worked on this tutorial are:
Aldren
Brad
Dan
Joanna
Michael
Master Real-World Python Skills With Unlimited Access to Real Python
Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:
Level Up Your Python Skills »
Master Real-World Python Skills With Unlimited Access to Real Python
Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:
Level Up Your Python Skills »
What Do You Think?
Rate this article:
Tweet Share Share Email
What’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment below and let us know.
Commenting Tips: The most useful comments are those written with the goal of learning from or helping out other students. and get answers to common questions in our support portal.