KishCom

Developer. Gamer. Yo-Yo Thrower.

Python Decorators

Still learning Python. It’s clean, easy to read but complex. One of the things I kept seeing across some of our back-end code are strings prefaced with “@” before a function definition. They’re called decorators and they were a mystery to me … until today. Succinctly put: decorators let you execute code before and after the function they decorate without needing to modify the function itself.

@immaDecorator
def immaFunction():
    #etc

Before we dive into what they do, we need to know two things about functions in python: Functions themselves can be assigned to a variable. Functions can be defined within other functions. Pretty straight forward:

def drink(type="redbull"):
    #Define a couple of functions inside our drink function
    def redbull(reaction="omgwtfbbq"):
        return reaction.upper()+"!!!!!"
    def coffee(reaction="omg"):
        return reaction.lower()+"!"

    #Decide what function to return
    if type == "redbull":
        return redbull
    else:
        return coffee

drinkMe = drink()
print drinkMe
#prints something like: <function "redbull" at 0xdeadbeef>
print drinkMe()
#prints OMGWTFBBQ!!!!!
print drink("not redbull")()
#calls the coffee function directly and prints "omg!"

#You can also pass functions as parameters
def doSomethingBefore(functionName):
    print "Herpty derp de derpity close... lets see what this function does:"
    print functionName()

doSomethingBefore(drinkMe)
#prints:
#Herpty derp de derpity close... lets see what this function does:
#OMGWTFBBQ!!!!!

Now; Decorators. A decorator is simply a function that expects another function as a parameter. Let’s do the work of a decorator manually:

def myDecorator(theFunctionToDecorate):
    #This nested function is going to wrap around the original function passed through myDecorator
    def wrapperAroundOriginalFunction():
        #Just a little proof we're doing something before
        print "Before the original function"
        theFunctionToDecorate()
        #Just a little proof we're doing something after
        print "After the original function"

    #It's important to note that the functions wrapperAroundOriginalFunction and theFunctionToDecorate are never actually excuted
    return wrapperAroundOriginalFunction

def justANormalFunction():
    print "I am a normal function. Please don't decorate me!! I have a family!!"

justANormalFunction()
#Outputs: I am a normal function. Please don't decorate me!! I have a family!!

decoratedNormalFunction = myDecorator(justANormalFunction)
decoratedNormalFunction()
#Outputs:
#Before the original function
#I am a normal function. Please don't decorate me!! I have a family!!
#After the original function

This is exactly what decorators do! In fact, we can change the last bit of code and then guess what? We’re using a decorator!

#Just Do this
@myDecorator
def justANormalFunction():
    print "I am a normal function. Please don't decorate me!! I have a family!!"

justANormalFunction()
#Now outputs:
#Before the original function
#I am a normal function. Please don't decorate me!! I have a family!!
#After the original function

#These guys aren't needed anymore! Our function justANormalFunction is now decorated!
#decoratedNormalFunction = myDecorator(justANormalFunction)
#decoratedNormalFunction()

If you’re getting this, you can see that Pythons decorators are similar to Java’s annotations and are purely syntactic sugar. However, the Wikipedia page for the programming pattern called decorators is explicit in pointing out: “Not to be confused with the concept of “decorators” in Python.”

Let’s look at one more slightly more complicated example. This time we’re going to build a delicious burger.

def theBun(func) :
    def wrapper() :
        print "|''''''|"
        func()
        print "|______|"
    return wrapper

def ingredients(func) :
    def wrapper() :
        print "-tomatoes-"
        func()
        print "-lettuce-"
    return wrapper

#Try changing the order of these decorators! You'll see that the order is important!
@theBun
@ingredients
def theBurger(food="--burger patty--") :
    print food

theBurger()
#If we didn't have the decorators on theBun()
#sandwich = theBun(ingredients(theBurger))
#sandwich()

That’s it! Please keep in mind that I’m still learning and if you see an error, or a better way to do something please let me know. I make these kinds blog posts mostly to help myself learn… I can’t do that if I’m learning the wrong stuff 😉

Back to KishCom.com
KishCom.Com

Table of Contents