Cara menggunakan decorators in python programiz

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
    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)
    
    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:

>>>

>>> 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 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:

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
.first_child at 0x7f599f1e2e18>

>>> second
.second_child at 0x7f599dad5268>

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 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:

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")
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:

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")
1

In 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")
2

However,

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")
3

If 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")
4

Syntactic 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")
5

So,

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 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

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")
6

Note: 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")
7

When 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")
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:

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")
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

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!'
1

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 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!'
2

Remove 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

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!'
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,

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!'
9

You 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()
0

Much 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 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:

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()
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

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()
2

This 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()
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

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()
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:

  1. Create a list of the positional arguments. 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")
    
    85 to get a nice string representing each argument.
  2. Create a list of the keyword arguments. The f-string formats each argument 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")
    
    86 where 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")
    
    87 specifier means 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")
    
    85 is used to represent the value.
  3. The lists of positional and keyword arguments is joined together to one signature string with each argument separated by a comma.
  4. 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()
5

Note 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()
6

This 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()
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:

Cara menggunakan decorators in python programiz

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()
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

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()
9

To 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
0

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")
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
1

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 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
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

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
3

Using 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
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() function
Printing from the second_child() function
Printing 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() function
Printing from the second_child() function
Printing 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() function
Printing from the second_child() function
Printing from the first_child() function
7

Using 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
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() function
Printing from the second_child() function
Printing 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

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
0

Decorating 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 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,

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
3

Observe 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
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
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
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

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
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:

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
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:

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
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:

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
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:

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
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:

  1. 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
    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, 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.
  2. In this case, the decorator was called with arguments. Return a decorator function that can read and return a function.
  3. 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
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:

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
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:

>>>

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
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:

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
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>
0

Remove 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

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
.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
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
.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

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
.first_child at 0x7f599f1e2e18>

>>> second
.second_child at 0x7f599dad5268>
5

We’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
.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() function
Printing from the second_child() function
Printing 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

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
.first_child at 0x7f599f1e2e18>

>>> second
.second_child at 0x7f599dad5268>
8

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()
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
.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

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'
0

It 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'
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

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'
3

The 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'
4

Note 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'
5

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()
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'
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

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'
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

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'
9

Note 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)
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

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)
1

You 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)
2

With 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)
3

Validating 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)
4

Here 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)
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:

  1. The list of keys that must be present in the JSON is given as arguments to the decorator.
  2. 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)
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
    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 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

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.

Cara menggunakan decorators in python programiz

Send Me Python Tricks »

About Geir Arne Hjelle

Cara menggunakan decorators in python programiz
Cara menggunakan decorators in python programiz

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:

Cara menggunakan decorators in python programiz

Aldren

Cara menggunakan decorators in python programiz

Brad

Cara menggunakan decorators in python programiz

Dan

Cara menggunakan decorators in python programiz

Joanna

Cara menggunakan decorators in python programiz

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.