Quick Intro to Decorators
You used a decorator in the previous lessons, but how does it work? Well, decorators themselves are functions. They are functions that, along with the
@ operator, combine to allow you to wrap another function. With the right syntax in place, you can write your own decorators. There are entire courses on it, but I’ll be giving you just a taste.
00:44 The pre-condition takes a timestamp, the wrapped function is then called, then the post-condition takes another timestamp and calculates the difference. Not being one to buck with tradition, let’s go do exactly that.
how_long() function that I’ve defined here in lines 4 through 13 is a decorator that times any function that it wraps. It’s built using an inner function to create a closure. Line 4 defines the function that is the decorator. It takes a single argument, which is the function that it is going to wrap. I’ve called that
01:21 The inner function is itself decorated. I’m not going to get into details here, but wrapping a function makes it appear differently on the stack and can make it hard to understand what failed if you get a traceback.
01:57 Prior to this, on line 7, is the pre-condition. For a timing function, that pre-condition stores the current time. Lines 9 and 10 are the post-condition that figures out how long the wrapped function took then prints out the result. As your wrapped function may return something, you need to return it as well, or you’ll be breaking the wrapped function. Because all of this is happening inside of an inner function, that inner function needs to also be returned to wire the decorator up correctly. This isn’t the easiest concept to get your head around, unless you’ve done work in languages with closures before. Don’t worry if you don’t get it a hundred percent. For many years, I would just grab an example and edit it to my needs rather than fully comprehending how it worked.
It wasn’t until I started playing with function closures that the light bulb actually went on. Scroll down here. At the bottom of my script, you’ll find my handy
pingala() function, which I have wrapped using the new
Did you expect all that? Remember that
pingala() is recursive. It calls itself. The decorator gets called each time. So instead of timing just the whole thing, you end up timing each execution of the function.
Notice that the last call took the longest time. It’ll be the sum total of all the preceding calls because of the recursion. And of course, that lonely
7 on the bottom is the printed results from line 26.
03:40 If all that wasn’t messy enough, you can actually give arguments to your decorators. This is done through yet another inner function. You’re going to your wrapper function in a wrapper function.
Let’s dissect this from the inside out. Look at the innermost function here, named
wrapper(), on line 7. Except for the fact that I’ve added a few more
print() statements, it’s the same as before. And like before, it’s decorated with the
@wraps decorator to take care of that pesky call stack mangling.
Peeling the onion one more layer, and you find a function called
decorator(). That takes an argument named
func. This is pretty much what the previous example called
how_long() did. The new bit is the extra layer on top.
This function wraps everything else. The arguments to this function will be the arguments that get passed to the decorator itself. In this case, I’m passing in an argument called
message, which I’ve included in the extra
print() statements on line 12.
05:45 That all was probably too fast. Decorators take a bit of mental processing. I know for the first twenty or so times I wrote them, I didn’t really get it, but you’ve seen the pattern and you can do as I did early on: copy and paste it and make it do what you want. Next up, I’ll add some time-based expiration to the LRU algorithm.
Become a Member to join the conversation.