For more information on topics covered in this lesson, check out these resources:
Taking and Returning Functions With Decorators
Another way of using the
return statement for returning functions is to write decorator functions.
00:07 A decorator function takes a function object as an argument and returns a function object. The decorator processes the decorated function in some way and then returns either that function object or replaces it with a function or other type of callable object. Decorators are useful when you need to add extra logic to an existing function but you don’t want to modify the original function itself.
00:35 There exist many decorators in Python. With classes and objects, you’ll see decorators for class methods, static methods, and properties. Web frameworks use decorators to control access, and you’ll see decorators and other libraries as well.
And you can write your own. For example, you might want to code a function to log its function calls, validate the arguments to a function, or measure the execution time of a function. Again, any situation where you need to add additional logic to a function without directly changing that function is a good place to use a function decorator. As an example, suppose you want to create a function that finds the execution time of another function. Based on what you saw in the last lesson, you could create a function to do the timing, which takes as an argument the function you want to time. Since it’s using timing functions, it needs to import the
01:39 I’ll have more to say about that near the end of the lesson. Next is to define a function whose purpose is to time the execution of another function. That function that you wish to time will be used as an argument to this function.
In it is an inner function, which will handle all of the processing. We find the system time that the function begins, then call the function provided as an argument to the outer function, allowing you to use any positional or keyword arguments that function was provided and then saving its return value to
02:16 Find the time when the function ends execution, then subtract the end and start times to find the duration. The inner function then returns whatever the result was.
The higher-ordered function returns this inner-function object, which remembers what function was provided as an argument much in the same way
multiply() from the previous lesson remembered the
2 and the
We then use a decorator—in this case,
@my_timer—to indicate that the function
my_timer() should decorate the function that follows.
I’ll explain what that means in a moment. In this case, it’s a function to compute the mean of a set of data. We have a time delay of
1 second, just to make determining the execution time more interesting.
And then we return the mean. Applying what you learned last lesson, you should be expecting
some_new_function = my_timer(delayed_mean), where we pass
delayed_mean as the argument to
This would create a new callable object made from the return value,
_timer, which remembers that it was made with
delayed_mean() as the argument
func. But that’s not how this is done in Python.
Here’s where that
@ notation comes in. When you indicate that
my_timer() is decorating
delayed_mean(), Python creates the function produced by
my_timer() passed with
delayed_mean as an argument and saves that back to the name
So now when you call
delayed_mean(), it will use the decorated function, which in this case, will time the execution of
So, let’s try this out. I’ll import our functions and call
delayed_mean() on some data.
04:22 I’m trying to use the same data we’ve seen in previous examples.
04:30 It took a little bit of time to run, but eventually the inner function produced the execution time of 1.00 something. And that’s going to vary from one function call to the next, depending on what your computer happens to be doing at the same time.
And then we got the mean of the data,
6.5, which for that set of data should always evaluate to six and a half.
So, what’s happening? Python runs decorator functions as soon as you import or run a module or script. In this case, when you called
delayed_mean(), you were really calling the object returned from
my_timer(), which here was the function object
The call to the decorated
delayed_mean() will return the mean of the original set of data and will also measure the execution time of the original
Notice that we haven’t changed any of the logic of
A decorator function essentially wraps itself around the function it decorates. If there was another function whose execution time you were interested in, all you would have to do is decorate that one with the
@my_timer decorator as well.
05:44 And remember, we did this to create additional logic—in this case, to time the execution of a function—without changing the function we wanted to time. And that is the role of decorators.
time(), we use the function
time() to help compute the execution time of a function call. This function is in a module called
time, which you saw me import, that provides a set of time-related functions.
time() function returns the number of seconds that has elapsed since some time designated as time equals zero on your computer. This time zero is referred to as the epoch time and its precise date and time depend on your computer and operating system. Finally, here are some additional Real Python resources you may want to look at for more detailed information on some of the material presented here.
06:39 Next, we’ll look at something called the factory pattern.
Become a Member to join the conversation.