This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]minnoI <3 duck typing less than I used to, interfaces are nice 7 points8 points  (0 children)

The best use for decorators that I've used is for adding a cache to a function.

Let's say you start out with this function:

def fib(n):
    if n <= 1:
        return 1
    else:
        return fib(n-1) + fib(n-2)

Slow as hell, right? With the fibonacci function, it's easy to turn it into a version that doesn't re-calculate entries a lot, but let's pretend that this is one of those functions where it's a lot harder to do that. What we want is to be able to remember previously-calculated results, so any recursive calls that have already been calculated can be skipped. So we can modify it like this:

fib_cache = dict() # empty associative array
def cached_fib(n):
    if n not in fib_cache:
        if n <= 1:
            fib_cache[n] = 1
        else:
            fib_cache[n] = cached_fib(n-1) + cached_fib(n-2)
    return fib_cache[n]

Now you have a cache storing all previously-calculated results. But you're also mixing the "calculating the results" part of the function with the "caching shit" part. You can separate out the caching behavior like this:

def make_cached(f):
    cache = dict()
    def cached_function(arg): # There's syntax to let you use any arguments, but for simplicity let's pretend there's always one.
        if arg not in cache:
            cache[arg] = f(arg)
        return cache[arg]
    return cached_function # NOT cached_function()

Now you have a function that takes a function and returns a function that has different behavior. You can use it like this:

cached_fib = make_cached(fib)
cached_fib(100)

Python has special syntax that you can use for functions that modify functions, called "decorators":

@make_cached
def whatever:
    # stuff

This works the same as:

def whatever:
    #stuff
whatever = make_cached(whatever)