Friday, January 17, 2014

Python Metaprogramming: A Brief Decorator Explanation

A brief explanation on how to think about Python decorators.  Given the following decorator definition:


def decorator(fn):
    def replacement(*a, **kw):
        ...
    return replacement

This usage of the decorator

@decorator
def fn():
    return

is functionaly equivalent to

def fn():
    return
fn = decorator(fn)

Note that fn is not being executed.  Instead decorator is being passed the callable object fn, and is in turn returning a callable object replacement which is then bound to the name fn.  Whether or not the original callable ever gets called is up to decorator and the replacement callable.

Another thing to consider, which often causes people problems, is the timing of the decorator's execution, which is to say during the loading of the module.  If you want to execute a particular piece of logic during fn's call, then that logic needs to be placed in the replacement callable, not in the decorator.

So now that everything is clear it's obvious that


@decorator
@make_decorator(args)
def fn():
    return


Is really just

def fn():
    return
fn = decorator(make_decorator(args)(fn))


Which means the first decorator in a stack is the last to be evaluated.

§

No comments:

Post a Comment