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
7 decoratordef my_decorator[func]: def wrapper[]: print["Something is happening before the function is called."] func[] print["Something is happening after the function is called."] return wrapper def say_whee[]: print["Whee!"] say_whee = my_decorator[say_whee]
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:
>>>
>>> def add_one[number]:
... return number + 1
>>> add_one[2]
3
In general, functions in Python may also have side effects rather than just turning an input into an output. The
def my_decorator[func]:
def wrapper[]:
print["Something is happening before the function is called."]
func[]
print["Something is happening after the function is called."]
return wrapper
def say_whee[]:
print["Whee!"]
say_whee = my_decorator[say_whee]
8 function is a basic example of this: it returns def my_decorator[func]:
def wrapper[]:
print["Something is happening before the function is called."]
func[]
print["Something is happening after the function is called."]
return wrapper
def say_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 adsFirst-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:
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
Here,
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
00 and def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
01 are regular functions that expect a name given as a string. The def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
02 function however, expects a function as its argument. We can, for instance, pass it the def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
00 or the def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
01 function:>>>
>>> greet_bob[say_hello]
'Hello Bob'
>>> greet_bob[be_awesome]
'Yo Bob, together we are the awesomest!'
Note that
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
05 refers to two functions, but in different ways: def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
02 and def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
07. The def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_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 def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_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:
def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_child[]:
print["Printing from the second_child[] function"]
second_child[]
first_child[]
What happens when you call the
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
10 function? Think about this for a minute. The output will be as follows:>>>
>>> parent[]
Printing from the parent[] function
Printing from the second_child[] function
Printing 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
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
10: they only exist inside the def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
10 function as local variables. Try calling def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_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
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
10, the inner functions def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
13 and def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
16 are also called. But because of their local scope, they aren’t available outside of the def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_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
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
10 function:def parent[num]:
def first_child[]:
return "Hi, I am Emma"
def second_child[]:
return "Call me Liam"
if num == 1:
return first_child
else:
return second_child
Note that you are returning
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
19 without the parentheses. Recall that this means that you are returning a reference to the function def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
19. In contrast def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_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
>>> second
The somewhat cryptic output simply means that the
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
22 variable refers to the local def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
13 function inside of def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
10, while def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
25 points to def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
16.You can now use
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
22 and def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_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
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
13. However, in this last example, you did not add parentheses to the inner functions—def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
19—upon returning. That way, you got a reference to each function that you could call in the future. Make sense?Remove adsSimple 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:
def my_decorator[func]:
def wrapper[]:
print["Something is happening before the function is called."]
func[]
print["Something is happening after the function is called."]
return wrapper
def say_whee[]:
print["Whee!"]
say_whee = my_decorator[say_whee]
Can you guess what happens when you call
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
31? Try it:>>>
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
0To 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:
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
1In effect, the name
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
32 now points to the def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
33 inner function. Remember that you return def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
34 as a function when you call def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
35:>>>
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
2However,
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
33 has a reference to the original def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
31 as def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
38, and calls that function between the two calls to def my_decorator[func]:
def wrapper[]:
print["Something is happening before the function is called."]
func[]
print["Something is happening after the function is called."]
return wrapper
def say_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
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_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:def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
3If you try to call
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
31 after bedtime, nothing will happen:>>>
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
4Syntactic Sugar!
The way you decorated
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
31 above is a little clunky. First of all, you end up typing the name def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_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
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
44 symbol, sometimes called the . The following example does the exact same thing as the first decorator example:def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
5So,
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
45 is just an easier way of saying def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
46. It’s how you apply a decorator to a function.Remove adsReusing 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
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
47 with the following content:def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
6Note: You can name your inner function whatever you want, and a generic name like
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_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 def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
49 prefix.You can now use this new decorator in other files by doing a regular import:
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
7When you run this example, you should see that the original
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
31 is executed twice:>>>
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
8Free 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:
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
9Unfortunately, running this code raises an error:
>>>
>>> greet_bob[say_hello]
'Hello Bob'
>>> greet_bob[be_awesome]
'Yo Bob, together we are the awesomest!'
0The problem is that the inner function
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
51 does not take any arguments, but def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
52 was passed to it. You could fix this by letting def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
51 accept one argument, but then it would not work for the def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
31 function you created earlier.The solution is to use
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
55 and def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
56 in the inner wrapper function. Then it will accept an arbitrary number of positional and keyword arguments. Rewrite def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
47 as follows:>>> greet_bob[say_hello]
'Hello Bob'
>>> greet_bob[be_awesome]
'Yo Bob, together we are the awesomest!'
1The
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
51 inner function now accepts any number of arguments and passes them on to the function it decorates. Now both your def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
31 and def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
60 examples works:>>>
>>> greet_bob[say_hello]
'Hello Bob'
>>> greet_bob[be_awesome]
'Yo Bob, together we are the awesomest!'
2Remove adsReturning 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!'
3Try to use it:
>>>
>>> greet_bob[say_hello]
'Hello Bob'
>>> greet_bob[be_awesome]
'Yo Bob, together we are the awesomest!'
4Oops, your decorator ate the return value from the function.
Because the
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
61 doesn’t explicitly return a value, the call def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
62 ended up returning def my_decorator[func]:
def wrapper[]:
print["Something is happening before the function is called."]
func[]
print["Something is happening after the function is called."]
return wrapper
def say_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
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
47 file:>>> greet_bob[say_hello]
'Hello Bob'
>>> greet_bob[be_awesome]
'Yo Bob, together we are the awesomest!'
5The 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!'
6Who 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!'
7The 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!'
8However, after being decorated,
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
31 has gotten very confused about its identity. It now reports being the def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
51 inner function inside the def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_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
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
47 again:>>> greet_bob[say_hello]
'Hello Bob'
>>> greet_bob[be_awesome]
'Yo Bob, together we are the awesomest!'
9You do not need to change anything about the decorated
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
31 function:>>>
def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_child[]:
print["Printing from the second_child[] function"]
second_child[]
first_child[]
0Much better! Now
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
31 is still itself after decoration.Technical Detail: The
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
68 decorator the function def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
73 to update special attributes like def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
74 and def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
75 that are used in the introspection.Remove adsA 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:
def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_child[]:
print["Printing from the second_child[] function"]
second_child[]
first_child[]
1This 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
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_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
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_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:def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_child[]:
print["Printing from the second_child[] function"]
second_child[]
first_child[]
2This decorator works by storing the time just before the function starts running [at the line marked
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
78] and just after the function finishes [at def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
79]. The time the function takes is then the difference between the two [at def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
80]. We use the function, which does a good job of measuring time intervals. Here are some examples of timings:>>>
def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_child[]:
print["Printing from the second_child[] function"]
second_child[]
first_child[]
3Run 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
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_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 def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_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
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_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:def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_child[]:
print["Printing from the second_child[] function"]
second_child[]
first_child[]
4The 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
85 to get a nice string representing each argument.def say_hello[name]: return f"Hello {name}" def be_awesome[name]: return f"Yo {name}, together we are the awesomest!" def greet_bob[greeter_func]: return greeter_func["Bob"]
- Create a list of the keyword arguments. The f-string formats each argument as
86 where thedef say_hello[name]: return f"Hello {name}" def be_awesome[name]: return f"Yo {name}, together we are the awesomest!" def greet_bob[greeter_func]: return greeter_func["Bob"]
87 specifier means thatdef say_hello[name]: return f"Hello {name}" def be_awesome[name]: return f"Yo {name}, together we are the awesomest!" def greet_bob[greeter_func]: return greeter_func["Bob"]
85 is used to represent the value.def say_hello[name]: return f"Hello {name}" def be_awesome[name]: return f"Yo {name}, together we are the awesomest!" def greet_bob[greeter_func]: return greeter_func["Bob"]
- 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:
def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_child[]:
print["Printing from the second_child[] function"]
second_child[]
first_child[]
5Note how the
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
84 decorator prints the signature and return value of the def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
90 function:>>>
def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_child[]:
print["Printing from the second_child[] function"]
second_child[]
first_child[]
6This example might not seem immediately useful since the
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_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:
def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_child[]:
print["Printing from the second_child[] function"]
second_child[]
first_child[]
7This 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
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
92 function, you can see the def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
84 decorator at work:>>>
def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_child[]:
print["Printing from the second_child[] function"]
second_child[]
first_child[]
8In this example, you get a decent approximation to the true value e = 2.718281828, adding only 5 terms.
Remove adsSlowing 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
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
94 decorator will sleep one second before it calls the decorated function:def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_child[]:
print["Printing from the second_child[] function"]
second_child[]
first_child[]
9To see the effect of the
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
94 decorator, you really need to run the example yourself:>>>
>>> parent[]
Printing from the parent[] function
Printing from the second_child[] function
Printing from the first_child[] function
0Note: The
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_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
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_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[] function
Printing from the second_child[] function
Printing from the first_child[] function
1The
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
98 decorator simply stores a reference to the decorated function in the global def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
99 dict. Note that you do not have to write an inner function or use def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_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 def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
99 dictionary already contains references to each function object that is registered as a plugin:>>>
>>> parent[]
Printing from the parent[] function
Printing from the second_child[] function
Printing from the first_child[] function
2The 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
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_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[] function
Printing from the second_child[] function
Printing from the first_child[] function
3Using the
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_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[] function
Printing from the second_child[] function
Printing from the first_child[] function
4While 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 adsFancy 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[] function
Printing from the second_child[] function
Printing from the first_child[] function
5In this class:
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.>>> greet_bob[say_hello] 'Hello Bob' >>> greet_bob[be_awesome] 'Yo Bob, together we are the awesomest!'
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[] function
Printing from the second_child[] function
Printing from the first_child[] function
6Let’s define a class where we decorate some of its methods using the and decorators from :
>>> parent[]
Printing from the parent[] function
Printing from the second_child[] function
Printing from the first_child[] function
7Using this class, you can see the effect of the decorators:
>>>
>>> parent[]
Printing from the parent[] function
Printing from the second_child[] function
Printing from the first_child[] function
8The 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[] function
Printing from the second_child[] function
Printing from the first_child[] function
9The 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
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_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
0Decorating a class does not decorate its methods. Recall that
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_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,
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_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 adsNesting 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
2Think about this as the decorators being executed in the order they are listed. In other words,
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
84 calls >>> greet_bob[say_hello]
'Hello Bob'
>>> greet_bob[be_awesome]
'Yo Bob, together we are the awesomest!'
38, which calls def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_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
3Observe the difference if we change the order of
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_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
4In this case,
>>> greet_bob[say_hello]
'Hello Bob'
>>> greet_bob[be_awesome]
'Yo Bob, together we are the awesomest!'
38 will be applied to def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
84 as well:>>>
Traceback [most recent call last]:
File "", line 1, in
NameError: name 'first_child' is not defined
5Decorators 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
7Think about how you could achieve this.
So far, the name written after the
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_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
8Typically, 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
9It 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:def parent[num]:
def first_child[]:
return "Hi, I am Emma"
def second_child[]:
return "Call me Liam"
if num == 1:
return first_child
else:
return second_child
0This
>>> 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:
def parent[num]:
def first_child[]:
return "Hi, I am Emma"
def second_child[]:
return "Call me Liam"
if num == 1:
return first_child
else:
return second_child
1Again,
>>> 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:
def parent[num]:
def first_child[]:
return "Hi, I am Emma"
def second_child[]:
return "Call me Liam"
if num == 1:
return first_child
else:
return second_child
2There 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
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.>>> greet_bob[say_hello] 'Hello Bob' >>> greet_bob[be_awesome] 'Yo Bob, together we are the awesomest!'
- The
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.>>> greet_bob[say_hello] 'Hello Bob' >>> greet_bob[be_awesome] 'Yo Bob, together we are the awesomest!'
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
7Just the result we were aiming for.
Remove adsBoth 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:def parent[num]:
def first_child[]:
return "Hi, I am Emma"
def second_child[]:
return "Call me Liam"
if num == 1:
return first_child
else:
return second_child
5Here, 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
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>>> greet_bob[say_hello] 'Hello Bob' >>> greet_bob[be_awesome] 'Yo Bob, together we are the awesomest!'
9, and some of the keyword arguments may have been changed from their default values. Thedef my_decorator[func]: def wrapper[]: print["Something is happening before the function is called."] func[] print["Something is happening after the function is called."] return wrapper def say_whee[]: print["Whee!"] say_whee = my_decorator[say_whee]
66 in the argument list means that the remaining arguments can’t be called as positional arguments.>>> greet_bob[say_hello] 'Hello Bob' >>> greet_bob[be_awesome] 'Yo Bob, together we are the awesomest!'
- 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:def parent[num]:
def first_child[]:
return "Hi, I am Emma"
def second_child[]:
return "Call me Liam"
if num == 1:
return first_child
else:
return second_child
6Compare 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:def parent[num]:
def first_child[]:
return "Hi, I am Emma"
def second_child[]:
return "Call me Liam"
if num == 1:
return first_child
else:
return second_child
7Recall 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:>>>
def parent[num]:
def first_child[]:
return "Hi, I am Emma"
def second_child[]:
return "Call me Liam"
if num == 1:
return first_child
else:
return second_child
8Stateful 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:
def parent[num]:
def first_child[]:
return "Hi, I am Emma"
def second_child[]:
return "Call me Liam"
if num == 1:
return first_child
else:
return second_child
9The 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
>>> second
0Remove adsClasses 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
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_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 def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_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
>>> second
1The
>>> 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
>>> second
2Therefore, 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
>>> second
3The
>>> 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 def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
33 function in our earlier examples. Note that you need to use the function instead of def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_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
>>> second
0More 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
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
94 using an optional def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_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
>>> second
5We’re using the boilerplate introduced in the section to make
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
94 callable both with and without arguments. The same recursive def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
96 function now sleeps two seconds between each count:>>> first = parent[1]
>>> second = parent[2]
>>> first
>>> second
6As before, you must run the example yourself to see the effect of the decorator:
>>>
>>> parent[]
Printing from the parent[] function
Printing from the second_child[] function
Printing from the first_child[] function
0Creating Singletons
A singleton is a class with only one instance. There are several singletons in Python that you use frequently, including
def my_decorator[func]:
def wrapper[]:
print["Something is happening before the function is called."]
func[]
print["Something is happening after the function is called."]
return wrapper
def say_whee[]:
print["Whee!"]
say_whee = my_decorator[say_whee]
9, def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_child[]:
print["Printing from the second_child[] function"]
second_child[]
first_child[]
04, and def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_child[]:
print["Printing from the second_child[] function"]
second_child[]
first_child[]
05. It is the fact that def my_decorator[func]:
def wrapper[]:
print["Something is happening before the function is called."]
func[]
print["Something is happening after the function is called."]
return wrapper
def say_whee[]:
print["Whee!"]
say_whee = my_decorator[say_whee]
9 is a singleton that allows you to compare for def my_decorator[func]:
def wrapper[]:
print["Something is happening before the function is called."]
func[]
print["Something is happening after the function is called."]
return wrapper
def say_whee[]:
print["Whee!"]
say_whee = my_decorator[say_whee]
9 using the def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_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
>>> second
8Using
def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_child[]:
print["Printing from the second_child[] function"]
second_child[]
first_child[]
08 returns def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_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
>>> second
9As you see, this class decorator follows the same template as our function decorators. The only difference is that we are using
def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_child[]:
print["Printing from the second_child[] function"]
second_child[]
first_child[]
12 instead of def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_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'
0It seems clear that
def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_child[]:
print["Printing from the second_child[] function"]
second_child[]
first_child[]
14 is indeed the exact same instance as def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_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'
1While the implementation is simple, its runtime performance is terrible:
>>>
>>> first[]
'Hi, I am Emma'
>>> second[]
'Call me Liam'
2To 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
def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_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
def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_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'
3The cache works as a lookup table, so now
def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_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'
4Note that in the final call to
def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_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 def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_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
def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_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'
5The
def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_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 def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_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
def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_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 def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_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'
6Adding 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
def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_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'
7The 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'
8This
def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_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'
9Note that you could have achieved something similar using function annotations:
def my_decorator[func]:
def wrapper[]:
print["Something is happening before the function is called."]
func[]
print["Something is happening after the function is called."]
return wrapper
def say_whee[]:
print["Whee!"]
say_whee = my_decorator[say_whee]
0However, 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
def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_child[]:
print["Printing from the second_child[] function"]
second_child[]
first_child[]
29. With def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_child[]:
print["Printing from the second_child[] function"]
second_child[]
first_child[]
29 installed [def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_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:>>>
def my_decorator[func]:
def wrapper[]:
print["Something is happening before the function is called."]
func[]
print["Something is happening after the function is called."]
return wrapper
def say_whee[]:
print["Whee!"]
say_whee = my_decorator[say_whee]
1You could also modify the decorator to return a
def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_child[]:
print["Printing from the second_child[] function"]
second_child[]
first_child[]
29 def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_child[]:
print["Printing from the second_child[] function"]
second_child[]
first_child[]
33 directly. Such a def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_child[]:
print["Printing from the second_child[] function"]
second_child[]
first_child[]
33 is made by multiplying a value with the unit. In def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_child[]:
print["Printing from the second_child[] function"]
second_child[]
first_child[]
29, units must be looked up in a def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_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:def my_decorator[func]:
def wrapper[]:
print["Something is happening before the function is called."]
func[]
print["Something is happening after the function is called."]
return wrapper
def say_whee[]:
print["Whee!"]
say_whee = my_decorator[say_whee]
2With the
def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_child[]:
print["Printing from the second_child[] function"]
second_child[]
first_child[]
37 decorator, converting units is practically effortless:>>>
def my_decorator[func]:
def wrapper[]:
print["Something is happening before the function is called."]
func[]
print["Something is happening after the function is called."]
return wrapper
def say_whee[]:
print["Whee!"]
say_whee = my_decorator[say_whee]
3Validating JSON
Let’s look at one last use case. Take a quick look at the following Flask route handler:
def my_decorator[func]:
def wrapper[]:
print["Something is happening before the function is called."]
func[]
print["Something is happening after the function is called."]
return wrapper
def say_whee[]:
print["Whee!"]
say_whee = my_decorator[say_whee]
4Here we ensure that the key
def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_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 def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_child[]:
print["Printing from the second_child[] function"]
second_child[]
first_child[]
39 decorator will do the job:def my_decorator[func]:
def wrapper[]:
print["Something is happening before the function is called."]
func[]
print["Something is happening after the function is called."]
return wrapper
def say_whee[]:
print["Whee!"]
say_whee = my_decorator[say_whee]
5In 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:
def my_decorator[func]:
def wrapper[]:
print["Something is happening before the function is called."]
func[]
print["Something is happening after the function is called."]
return wrapper
def say_whee[]:
print["Whee!"]
say_whee = my_decorator[say_whee]
6Conclusion
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
68 to look more like the decorated function.def say_hello[name]: return f"Hello {name}" def be_awesome[name]: return f"Yo {name}, together we are the awesomest!" def greet_bob[greeter_func]: return greeter_func["Bob"]
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
def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_func["Bob"]
55 and def say_hello[name]:
return f"Hello {name}"
def be_awesome[name]:
return f"Yo {name}, together we are the awesomest!"
def greet_bob[greeter_func]:
return greeter_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 def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_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
def parent[]:
print["Printing from the parent[] function"]
def first_child[]:
print["Printing from the first_child[] function"]
def second_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 ArneEach 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 EmailWhat’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.