Decorators are a simple, handy way of implementing certain features in any language, but many people find them perplexing. When you start talking about functions that generate decorators, it gets a little twisty; when you then make helpful functions to create functions that create decorators, you might have to stop for a minute to unclench your brain. Really, though, the concept is quite simple if you take it step by step, so here it is (after the jump), hopefully demystified.



Decorators

def squared(f): def inner(*args): result = f(*args) return result**2 return inner def timestwo(n): return n*2 timestwo = squared(timestwo)

squared()

timestwo()

timestwo

inner

timestwo

@squared def timestwo(n): ...

Decorator Factories

squared()

cubed()

timed()

raisetopower

decorator

inner

def raisetopower(exponent): def decorator(f): def inner(*args): result = f(*args) return result**exponent return inner return decorator

raisetopower(2)

squared()

squared = raisetopower(2) cubed = raisetopower(3)

@raisetopower(2) def timestwo(n): return n*2

timestwo = raisetopower(2)(timestwo)

raisetopower(2)==squared

Decorator Factory Factories

@GET('^/.*$') def onGetAnything(self): ...

GET

GET

POST

PUT

DELETE

def decorator_factory_factory(method): def decorator_factory(regex): def decorator(f): def inner(*args, **kwargs): # do stuff with f(*args, **kwargs) # and method and regex return inner return decorator return decorator_factory GET = decorator_factory_factory('GET') PUT = decorator_factory_factory('PUT')

@decorator_factory_factory('GET')('^/.*$') def onGetAnything(self): ...

For the uninitiated, a decorator is a function that replaces another function, adding extra functionality along the way. Here is a trivial example in Python:accepts a function and returns a function, which is the fundamental characteristic of a decorator. We then decorateby passing it through the decorator and replacing the reference to the original function with the result. After all this has executed,is now actually the closure, which keeps a reference to the originalin the decorator's scope so it can call it and mess with the result.In Python you can use nice decorator syntax:Which is synonymous with the manual call to the decorator and reassignment.So let's say you want a more flexible decorator that can raise to any power, not just 2. We could individually defineand so on, but it's much easier just to create a function that returns the decorator we need. We'll make a decorator factory,, that returns a decorator,, that returns a function,, which will replace the decorated function. Again, we'll use closures all the way down, so we keep all the information we need in scope:Sincewill return what is essentiallyfrom above, we could do:But it is much easier just to do:This may look strange to some, but bear in mind that it is functionally identical to:And since, we've recreated the same situation we had above, only we can now pass in any exponent we want.Naturally the same technique, creating and returning a closure wrapping the original function, can be nested indefinitely; at some point it becomes unreadable. Personally, I would never have multiple calls on the same line, so I draw the line at actually calling decorator factories. I have, however, found uses for decorator factory factories, which you would need when you wanted a reusable decorator factory with some information frozen inside. For example, in txrestapi , I needed decorators that tied a function to both a request method and a regular expression, like:itself is just a decorator factory, but I defined(and, and, and) using a decorator factory factory:Now, I could have used the function to do:But as I said above, you reach a point where it's totally unreadable, so draw the line appropriately.So that's that: decorators, and functions that create decorators, and functions that create functions that create decorators, all unpacked. Really it's just the same technique, replacing a function with a closure wrapping that function, over and over.