Understanding Python decorators
A while back, I was hanging out on some Python forum and someone posted a comment about not being able to wrap their head around decorators. I spent some time on what I thought was a helpful comment, and that was that. Recently I figured it'd be worthwhile to expand that comment a bit. So, here we go.
First, it helpes to understand that python variables are not boxes in which you store values, but rather name tags that you hang onto objects. Please don't move on until you grok this, it's the central principle to decorators.
Second, in Python, functions are objects. Function names themselves are just name tags hung onto their respective function objects.
Finally, here's a simple definition of a decorator: It's just a function. Seriously. Nothing magic about it all. It takes another function as an argument (the function being decorated). It returns yet another function object.
If that sounded like I just said, "Function function function function function," (which is how I felt when trying to wrap my head around decorators), here's a small python program that may help out:
If we'd run this code in the interactive interpreter, here's the output we'd get:
That above syntax is what made decorators click for me. All we've done here is taken the
orig_function nametag, and hung it onto the
third_function object (remember,
third_func). So when we call
orig_function() now, what we're really calling is
I used the above syntax (
a=decorator(b) ) until I was sure I understood how decorators worked before I switched to using the shorter @ syntax.
is equivalent to
which isn't as intuitive and is probably the main roadblock to understanding decorators right at first, but nicer to use once you've got the hang of things.
In this example, we didn't do anything in the
my_decorator function with the argument that was passed in (
orig_function ), but we could have called it and returned something other than third_func, depending on what orig_function returned. Remember, a decorator is just a function, you can do anything inside a decorator you'd do in a vanilla Python function. It just needs to take a function as a parameter, and return another function.