In this lesson, you'll make your first actual decorator. To do this, you'll piece together what you learned in the previous lessons.
Function Objects As Arguments
The difference to what you wrote before is that you're passing a function as an argument instead of a string message. You remember that functions are nothing special, which means that you can pop them as an argument just as you did with the message string before:
def outer_func(initial_func):
def inner_func():
return initial_func()
return inner_func
You can see that the only difference here is that you're passing a function object, initial_func, as the parameter to outer_func(). Instead of printing out a message, you return a call to that initial_func().
Build An Empty Decorator
When working with decorators, there are three different functions that you need to distinguish between:
- Decorator function
- Wrapper function
- Initial function
Rename Functions for Clarity
To move towards a more generalized use of decorators, you'll now rename the three functions that you're using in this example accordingly:
def decorator_func(initial_func):
def wrapper_func():
return initial_func()
return wrapper_func # returns the wrapper func ready to be executed
In this example of an empty decorator, you're defining a decorator function decorator_func() that takes an initial function initial_func() as a parameter. This initial_func() can be any function object that you want to pass. Inside the decorator_func(), you're defining another function called wrapper_func() that returns a call to the initial_func(). This means that when the wrapper_func() gets called, it will also call your initial_func() and return its output.
Tasks
- Take a moment to think about what could be an advantage of this construct.
- Draw the structure of your function burrito in your notebook.
- When does your
initial_func()get called?
Usually, the wrapper_func() would do something before returning the call to your initial_func(), and those actions could change the output of your initial_func(). You'll get to that in the next step.
Apply An Empty Decorator
You can think of this decorator as empty because the wrapper function doesn't change any behavior of the initial function.
This means that applying it to an initial function is a bit boring for now, but you'll give it a go anyway. After defining the decorator_func() as described above, you can now apply it to another function:
def prettify():
print("flowers")
decorated_pretty = decorator_func(prettify)
decorated_pretty()
Explain the Code
You've written a couple of new lines of code, so you'll walk over what each part does before moving on:
- First, you define a function called
prettify()that, in this case, only prints out a short message. - Next, you pass the
prettifyfunction object to your decorator functiondecorator_func()and assign the output to a new variable calleddecorated_pretty. - Finally, you can now call
decorated_pretty(), which is the function objectwrapper_funcreturned by yourdecorator_func(), which will then call your initial functionprettify().
After your code jumps through all the hoops you set up for it and does a great job at it, you'll get exactly the same output that you'd have gotten if you called prettify() right away.
Sounds pointless? Well, so far, it is. However, it'll make more sense once you introduce some additional functionality inside of your wrapper_func(), which you'll do in the next lesson.
Summary: Test an Empty Python Decorator
- Function objects can be passed as arguments to functions
- Functions passed as arguments can be called inside of the function it is passed to
- With decorators, there exists the decorator, wrapper, and initial function