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 😉