# Decorators in Python¶

Shrayas (@shrayasr)

# Recap on Functions¶

## 1. Functions can be defined inside functions¶

In [26]:
def say_something(thought):

def _thought_makes_sense(thought):
return True

if _thought_makes_sense(thought):
return thought

print say_something("That person stinks!")

That person stinks!


## 2. Functions are Lexically scoped¶

In [27]:
def say_something(thought):

def _thought_makes_sense():
print "HMMM \""+thought+"\"... I Wonder..."
return True

if _thought_makes_sense():
return thought

print say_something("That person stinks!")

HMMM "That person stinks!"... I Wonder...
That person stinks!


## 3. Functions can be passed as arguments to other Functions¶

In [28]:
def say_something(thought, tone=None):

def yell(thought):
return thought.upper()

def whisper(thought):
return "Shhh... " + thought.lower()

print say_something("where are you babu?", tone=yell)
print say_something("Lets go get an ice cream!", tone=whisper)

WHERE ARE YOU BABU?
Shhh... lets go get an ice cream!


## 4. Functions can be returned from functions¶

In [29]:
def mood_maker(mood):

def happy(thought):
return "^_^ " + thought + " ^_^"

return thought + " ... :'( sniff"

moods = {
"HAPPY": happy,
}

return moods[mood.upper()]

say_happily = mood_maker("happy")
print say_happily("Hello!")

print say_sadly("I lost my favourite pen!")

^_^ Hello! ^_^
I lost my favourite pen! ... :'( sniff


## Lets add some masala to a function!¶

In [30]:
def masala_adder(func):

return "^#!^^@) [" + func() + "] [email protected]&$**^&$"

def say_hi():
return "Hii"

print say_hi()
print masala_say_hi()

Hii
^#!^^@) [Hii] [email protected]&$**^&$


## Masala, Decorators style!¶

In [31]:
@masala_adder
def say_hello():
return "Hello"

say_hello()

Out[31]:
'^#!^^@) [Hello] [email protected]&$**^&$'

## Wait, what?¶

In [32]:
say_hello = masala_adder(say_hello)


Decorators are just syntactic sugar on a function that takes a function and returns a replacement function

A.K.A



Wrappers

In [33]:
# Define decorator here
def decorator(func):

def new_func():
print "before"
func()
print "after"

return new_func

@decorator
def say_hello():
print "Hello!"

say_hello()

before
Hello!
after


## Lets make some owls!¶

In [34]:
def ears(func):
def ears_wrapper():
owl_string = " /\\_/\\\n"
owl_string += func()
return owl_string

return ears_wrapper

def eyes(func):
def eyes_wrapper():
owl_string = " (O.O) \n"
owl_string += func()
return owl_string

return eyes_wrapper

def body(func):
def body_wrapper():
owl_string = " (= =)\n"
owl_string += func()
return owl_string

return body_wrapper

def legs(func):
def legs_wrapper():
owl_string = "  ^^^\n"
owl_string += func()
return owl_string

return legs_wrapper

@ears
@eyes
@body
@legs
def owl():
return "hoot hoot"

print owl()

 /\_/\
(O.O)
(= =)
^^^
hoot hoot

In [35]:
owl = ears(eyes(body(legs(owl))))


## Arguments to decorated functions¶

In [36]:
def decorator(func):
def new_func(arg1, arg2):
print "I R HAZ ARGZ! [%s, %s]" % (arg1, arg2)
return func(arg1, arg2)
return new_func

@decorator
def sum_of_squares(num1, num2):
print num1*num1 + num2*num2

sum_of_squares(1,2)

I R HAZ ARGZ! [1, 2]
5

In [37]:
def logger(func):
def decorated_function(*args, **kwargs):
print "[INFO][Arguments]", args, kwargs
return func(*args, **kwargs)
return decorated_function

@logger
def sum_of_squares(num1, num2):
print num1*num1 + num2*num2

@logger

sum_of_squares(1,2)

[INFO][Arguments] (1, 2) {}
5
[INFO][Arguments] ('/etc/init.d',) {'record_delimiter': ';'}


## Exercise: Lets use decorators!¶

Let us write a small cache!

In [38]:
_cache = {}

"""
1_2 => 3
"""

def cache(func):

def new_func(*args, **kwargs):

key_str = ""
for arg in args:
key_str += str(arg) + "_"
key_str = key_str[:-1]

val = _cache.get(key_str)
if not val:
val = func(*args)
_cache[key_str] = val
return val

return new_func

In [39]:
import time

@cache
time.sleep(2)
return a+b

In [40]:
add_2_nos(1,2)

Out[40]:
3
In [41]:
add_2_nos(1,2)

Out[41]:
3
In [42]:
add_2_nos(10,20)

Out[42]:
30
In [43]:
add_2_nos(10,20)

Out[43]:
30
In [44]:
_cache

Out[44]:
{'10_20': 30, '1_2': 3}