Understanding Decorator in Python

Decorator in Python

Many times when you read the code, you will find some annoying synax like @somefunction. What does that mean? That is the decorator in python. The synax for the decorator has the following definition and function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@decorator
var = some_value

# which would be the same as

var = decorator(some_value)

# and can be chained as well:

@decorator
@decorator_2
var = some_value

# which would be

var = decorator(decorator_2(some_value))

the main advantage is that you can use the decorator as a standardized way to create variables that have the same behaviour, instead
of havng to do that using methods. I think that a lot can be gained by specifying a decorator that can decorate variables or properties.

BUT, just look at the synax again, the decorator actually help developer to initialize the variable before the invoke of the function. for example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
list_function=[]

def add_function_lists(f):
list_function.append(f)
print("this is decorator")
return f

@add_function_lists
def a():
print("this is function a")

@add_function_lists
def b():
print("this is function b")


```

The above code has no initilization or instance to call the function. But can you guess the result when we codpy the code into the python complier?

```python
this is decorator
this is decorator

if we check the list_function, we will find

1
2
>>>list_function
>>>[<function __main__.a>, <function __main__.b>]

In order to have a deeper understanding of the decorator, remember that functions are objects in Python.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
def shout(word="yes"):
return word.capitalize()+"!"

print shout()
# outputs : 'Yes!'

# As an object, you can assign the function to a variable like any
# other object

scream = shout

# Notice we don't use parentheses: we are not calling the function, we are
# putting the function "shout" into the variable "scream".
# It means you can then call "shout" from "scream":

print scream()
# outputs : 'Yes!'

# More than that, it means you can remove the old name 'shout', and
# the function will still be accessible from 'scream'

del shout
try:
print shout()
except NameError, e:
print e
#outputs: "name 'shout' is not defined"

print scream()
# outputs: 'Yes!'

Another interesting property of Python functions is they can be defined… inside another function!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

def talk():

# You can define a function on the fly in "talk" ...
def whisper(word="yes"):
return word.lower()+"..."

# ... and use it right away!

print whisper()

# You call "talk", that defines "whisper" EVERY TIME you call it, then
# "whisper" is called in "talk".
talk()
# outputs:
# "yes..."

# But "whisper" DOES NOT EXIST outside "talk":

try:
print whisper()
except NameError, e:
print e
#outputs : "name 'whisper' is not defined"*

Usually, people use decorator to define a insider functions for invoke. The following is the example without decorator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

# A decorator is a function that expects ANOTHER function as parameter
def my_shiny_new_decorator(a_function_to_decorate):

# Inside, the decorator defines a function on the fly: the wrapper.
# This function is going to be wrapped around the original function
# so it can execute code before and after it.
def the_wrapper_around_the_original_function():

# Put here the code you want to be executed BEFORE the original
# function is called
print "Before the function runs"

# Call the function here (using parentheses)
a_function_to_decorate()

# Put here the code you want to be executed AFTER the original
# function is called
print "After the function runs"

# At this point, "a_function_to_decorate" HAS NEVER BEEN EXECUTED.
# We return the wrapper function we have just created.
# The wrapper contains the function and the code to execute before
# and after. It's ready to use!
return the_wrapper_around_the_original_function

# Now imagine you create a function you don't want to ever touch again.
def a_stand_alone_function():
print "I am a stand alone function, don't you dare modify me"

a_stand_alone_function()
#outputs: I am a stand alone function, don't you dare modify me

# Well, you can decorate it to extend its behavior.
# Just pass it to the decorator, it will wrap it dynamically in
# any code you want and return you a new function ready to be used:

a_stand_alone_function_decorated = my_shiny_new_decorator(a_stand_alone_function)
a_stand_alone_function_decorated()
#outputs:
#Before the function runs
#I am a stand alone function, don't you dare modify me
#After the function runs

When we start using decorator, the world becomes simple:

1
2
3
4
5
6
7
8
9
@my_shiny_new_decorator
def another_stand_alone_function():
print "Leave me alone"

another_stand_alone_function()
#outputs:
#Before the function runs
#Leave me alone
#After the function runs