It can be useful to encapsulate a function in another function. A decorator is a metafunction that returns another function. This lesson provides an example of how useful this can be.
- [Instructor] A decorator is a form of metaprogramming and it can be described as a special type of function that returns a wrapper function. Here in Komodo I've opened working copy of hello.py from chapter seven of the exercise files. First it's important to realize that in Python everything is an object. So a function is a type of object. So if I'm here and I define a function and I run it.
So I'm calling the function and you notice that it prints out this is f1. But what's interesting here is I can say x equals f1 with the parenthesis and now I'm assigning that function object to the variable x. But everything is an object, so a variable is an object too and I can simply call the function f1 by calling x. So when I save and run, I get exactly the same result. So this means that I can do silly things like this.
So now what I've done, is I've created a function f1 that contains another function f2 and I cannot call f2 directly, because it's scope is inside the function. Remember functions do define scope, locks don't define scope, functions do. And so when I run this, you notice it runs f2, because the return value from f1 is the object f2. And so take that return value, I assign it to x and I call x as if it were a function.
I cannot call f2 directly. If I try this, I get a syntax error. NameError, f2 is not defined, because f2 only exists inside the scope of f1. So f1 can be said to be a wrapper for f2. Now we're going to create a decorator, 'cause this is where this gets actually very interesting. I'll define f3 down here, outside of the scope of the function and I print this is f3.
Now f1 is going to take a argument, which it's going to use as a function and I'm going to print, this is before function call and this is after the function call. And we're going to call f, which is the function that we passed in the argument list. And now down here, I'm going to call f1 with f3 as an argument, but I'm going to take the return value and I'm going to call on that return value.
Now you remember the return value is f2, which calls the argument that's passed in. Now when I run this, you notice we get this before the function call, then it calls f3 because it was passed. But here's where it becomes a decorator. If I assign this to f3 and then call f3, now f3 in its original form is no longer available, only the wrapper is available and when I run this you see it's the wrapper. Because this is so meta and self-referential and weird and recursive, instead there's a short cut for it, which simply looks like this.
Now when I call f3, we get that result. So it takes f3, this syntax here which is called a decorator, it takes the function which is defined directly after it, so the syntax is you have to have the decorator, followed directly by the function definition and it takes that function and it passes it as an argument to the decorator function and it returns and it assigns that name of f3 to the wrapper itself.
And so now I can't call f3 directly, I can only call it through the wrapper and f3 now is wrapped inside that decorator function. So how is this useful, because it's awful meta and weird looking right? Well here's how it's useful. Here's a working copy of decorator.py from chapter seven of the exercise files. I'm just going to drop this bottom pane here so that you can see what it is. I have a function called elapsed_time which wraps the past function in two time stamps and prints out the elapsed time.
It runs the function and prints out the elapsed time. I have this function here called big_sum that adds up a whole lot of numbers together and prints the sum. And then down here I simply call big_sum, but because big_sum has this decorator, it's actually wrapped in the elapsed_time wrapper. And so I'll save this and I'll run it and you see there's the result. Big_sum is that big number and it took 1.434 milliseconds to calculate that. So that's one practical example of what a decorator is good for.
- Python anatomy
- Types and values
- Conditionals and operators
- Building loops
- Defining functions
- Python data structures: lists, tuples, sets, and more
- Creating classes
- Handling exceptions
- Working with strings
- File input/output (I/O)
- Creating modules
- Integrating a database with Python db-api